Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ModelBuilder: Exception when KeyAttribute used with inheritance #5898

Closed
beho opened this issue Jun 29, 2016 · 2 comments
Closed

ModelBuilder: Exception when KeyAttribute used with inheritance #5898

beho opened this issue Jun 29, 2016 · 2 comments
Labels
closed-fixed The issue has been fixed and is/will be included in the release indicated by the issue milestone. type-bug
Milestone

Comments

@beho
Copy link

beho commented Jun 29, 2016

Steps to reproduce

In my model there are transactions of following types:

public enum BoxOfficeTransactionType : byte
{
    Sale = 0,
    Refund = 1,

    StatusChange = 5,

    Transaction = 10
}

Base class looks like this:

[Table( "BoxOfficeTransactions" )]
public class BoxOfficeTransaction : ICreationTrackingModel
{
    [Key, DatabaseGenerated( DatabaseGeneratedOption.Identity )]
    public int Id { get; set; }

    public int BoxOfficeId { get; set; }

    public BoxOfficeTransactionType Type { get; set; }

    public BoxOfficeStatus Status { get; set; }

    public string CreatedById { get; set; }

    public decimal Amount { get; set; }

    public decimal Balance { get; set; }

    [MaxLength( 512 )]
    public string Description { get; set; }

    [MaxLength( 512 )]
    public string Note { get; set; }

    public DateTime CreatedAt { get; set; }

    public BoxOffice BoxOffice { get; set; }
    public User CreatedBy { get; set; }
}

and these are descendants:

public class BoxOfficeSaleTransaction : BoxOfficeTransaction
{
    public int PurchaseId { get; set; }

    public Purchase Purchase { get; set; }
}
public class BoxOfficeRefundTransaction : BoxOfficeTransaction
{
    public Guid TicketId { get; set; }

    public Ticket Ticket { get; set; }
}
public class BoxOfficeStatusChangeTransaction : BoxOfficeTransaction
{
}

In ApplicationDbContext is the following:

public DbSet<BoxOfficeTransaction> BoxOfficeTransactions { get; set; }
public DbSet<BoxOfficeSaleTransaction> BoxOfficeSaleTransactions { get; set; }
public DbSet<BoxOfficeRefundTransaction> BoxOfficeRefundTransactions { get; set; }
public DbSet<BoxOfficeStatusChangeTransaction> BoxOfficeStatusChangeTransactions { get; set; }

...

builder.Entity<BoxOfficeTransaction>()
    .HasDiscriminator( m => m.Type )
    .HasValue<BoxOfficeTransaction>( BoxOfficeTransactionType.Transaction )
    .HasValue<BoxOfficeSaleTransaction>( BoxOfficeTransactionType.Sale )
    .HasValue<BoxOfficeRefundTransaction>( BoxOfficeTransactionType.Refund )
    .HasValue<BoxOfficeStatusChangeTransaction>( BoxOfficeTransactionType.StatusChange );

builder.Entity<Purchase>()
    .HasOne( p => p.Transaction )
    .WithOne( tx => tx.Purchase )
    .HasForeignKey<BoxOfficeSaleTransaction>( tx => tx.PurchaseId )
    .OnDelete( DeleteBehavior.Restrict );

builder.Entity<BoxOfficeSaleTransaction>()
    .HasIndex( tx => tx.PurchaseId )
    .IsUnique();

builder.Entity<Ticket>()
    .HasOne( t => t.RefundTransaction )
    .WithOne( tx => tx.Ticket )
    .HasForeignKey<BoxOfficeRefundTransaction>( tx => tx.TicketId )
    .OnDelete( DeleteBehavior.Restrict );

builder.Entity<BoxOfficeRefundTransaction>()
    .HasIndex( tx => tx.TicketId )
    .IsUnique();

The issue

When trying to create initial migration I get the following:

System.InvalidOperationException: The derived type 'BoxOfficeRefundTransaction' cannot have KeyAttribute on property 'Id' since primary key can only be declared on the root type. 
    at Microsoft.EntityFrameworkCore.Metadata.Conventions.Internal.KeyAttributeConvention.Apply(InternalPropertyBuilder propertyBuilder, KeyAttribute attribute, PropertyInfo clrProperty) 
   at Microsoft.EntityFrameworkCore.Metadata.Conventions.Internal.PropertyAttributeConvention`1.Apply(InternalPropertyBuilder propertyBuilder)
    at Microsoft.EntityFrameworkCore.Metadata.Conventions.Internal.ConventionDispatcher.OnPropertyAdded(InternalPropertyBuilder propertyBuilder) 
    at Microsoft.EntityFrameworkCore.Metadata.Internal.EntityType.AddProperty(Property property, Boolean runConventions) 
    at Microsoft.EntityFrameworkCore.Metadata.Internal.InternalEntityTypeBuilder.Property(Property existingProperty, String propertyName, Type propertyType, PropertyInfo clrProperty, Nullable`1 configurationSource) 
    at Microsoft.EntityFrameworkCore.Metadata.Internal.InternalEntityTypeBuilder.Property(String propertyName, Type propertyType, PropertyInfo clrProperty, Nullable`1 configurationSource) 
    at Microsoft.EntityFrameworkCore.Metadata.Internal.InternalEntityTypeBuilder.Property(PropertyInfo clrProperty, ConfigurationSource configurationSource) 
    at Microsoft.EntityFrameworkCore.Metadata.Conventions.Internal.PropertyDiscoveryConvention.Apply(InternalEntityTypeBuilder entityTypeBuilder) 
    at Microsoft.EntityFrameworkCore.Metadata.Conventions.Internal.PropertyDiscoveryConvention.Apply(InternalEntityTypeBuilder entityTypeBuilder, EntityType oldBaseType) 
    at Microsoft.EntityFrameworkCore.Metadata.Conventions.Internal.ConventionDispatcher.OnBaseEntityTypeSet(InternalEntityTypeBuilder entityTypeBuilder, EntityType previousBaseType) 
    at Microsoft.EntityFrameworkCore.Metadata.Internal.InternalEntityTypeBuilder.HasBaseType(EntityType baseEntityType, ConfigurationSource configurationSource) 
    at Microsoft.EntityFrameworkCore.Metadata.Conventions.Internal.ConventionDispatcher.OnEntityTypeAdded(InternalEntityTypeBuilder entityTypeBuilder) 
    at Microsoft.EntityFrameworkCore.Metadata.Conventions.Internal.RelationshipDiscoveryConvention.Apply(InternalEntityTypeBuilder entityTypeBuilder) 
    at Microsoft.EntityFrameworkCore.Metadata.Conventions.Internal.ConventionDispatcher.OnEntityTypeAdded(InternalEntityTypeBuilder entityTypeBuilder) 
    at Microsoft.EntityFrameworkCore.Metadata.Internal.Model.AddEntityType(EntityType entityType, Boolean runConventions) 
    at Microsoft.EntityFrameworkCore.Metadata.Internal.InternalModelBuilder.Entity(Type type, ConfigurationSource configurationSource, Boolean runConventions) 
    at Microsoft.EntityFrameworkCore.ModelBuilder.Entity(Type type) 
    at Microsoft.EntityFrameworkCore.Infrastructure.ModelSource.FindSets(ModelBuilder modelBuilder, DbContext context) 
    at Microsoft.EntityFrameworkCore.Infrastructure.Internal.RelationalModelSource.FindSets(ModelBuilder modelBuilder, DbContext context) 
    at Microsoft.EntityFrameworkCore.Infrastructure.ModelSource.CreateModel(DbContext context, IConventionSetBuilder conventionSetBuilder, IModelValidator validator) 
    at System.Collections.Concurrent.ConcurrentDictionary`2.GetOrAdd(TKey key, Func`2 valueFactory) 
    at Microsoft.EntityFrameworkCore.Internal.DbContextServices.CreateModel() 
   at Microsoft.EntityFrameworkCore.Internal.LazyRef`1.get_Value()
    at Microsoft.Extensions.DependencyInjection.ServiceProvider.ScopedCallSite.Invoke(ServiceProvider provider) 
    at Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetService[T](IServiceProvider provider) 
   at Microsoft.Extensions.DependencyInjection.ServiceProvider.TransientCallSite.Invoke(ServiceProvider provider) 
    at Microsoft.Extensions.DependencyInjection.ServiceLookup.ConstructorCallSite.Invoke(ServiceProvider provider) 
    at Microsoft.Extensions.DependencyInjection.ServiceProvider.TransientCallSite.Invoke(ServiceProvider provider) 
    at Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService(IServiceProvider provider, Type serviceType) 
    at Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService[T](IServiceProvider provider) 
    at Microsoft.EntityFrameworkCore.Design.MigrationsOperations.AddMigration(String name, String outputDir, String contextType) 
    at Microsoft.EntityFrameworkCore.Tools.Cli.MigrationsAddCommand.Execute(CommonOptions commonOptions, String name, String outputDir, String context, String environment, Action`1 reporter) 
    at Microsoft.EntityFrameworkCore.Tools.Cli.MigrationsAddCommand.<>c__DisplayClass0_0.<Configure>b__0() 
    at Microsoft.Extensions.CommandLineUtils.CommandLineApplication.Execute(String[] args) 
    at Microsoft.EntityFrameworkCore.Tools.Cli.Program.Main(String[] args) 
 The derived type 'BoxOfficeRefundTransaction' cannot have KeyAttribute on property 'Id' since primary key can only be declared on the root type. 

The exception is only thrown for BoxOfficeSaleTransaction and BoxOfficeRefundTransaction. When I remove these from the model (only BoxOfficeStatusChangeTransaction remains) then migration is generated.

When I comment out the Key data annotation for Id property in BoxOfficeTransaction and use following fluent configuration, migration is generated without error.

builder.Entity<BoxOfficeTransaction>()
    .HasKey( tx => tx.Id );
builder.Entity<BoxOfficeTransaction>()
    .Property( tx => tx.Id )
    .ValueGeneratedOnAdd();

Further technical details

EF Core version: 1.0.0 (RTM)
Operating system: Windows 10
Visual Studio version: VS 2015 Update 3

I can provide full model by private channel.

@rowanmiller
Copy link
Contributor

Looks like we may not be filtering out inherited annotations. Putting in 1.0.1 as it seems Key with inheritance may be broken. If it is more constrained, we should reevaluate if it is patch worthy.

@rowanmiller rowanmiller added this to the 1.0.1 milestone Jun 29, 2016
@rowanmiller rowanmiller changed the title KeyAttribute can only be declared on the root type KeyAttribute fails when derived entities are included in model Jun 29, 2016
@AndriySvyryd
Copy link
Member

@beho I'm unable to repro this. Could you send the full model to Andriy.Svyryd@microsoft.com?

@rowanmiller rowanmiller removed the pri0 label Jul 6, 2016
AndriySvyryd added a commit that referenced this issue Jul 6, 2016
…n in a different order. This can cause the KeyAttributeConvention to run on derived types before the base. Move the check to validation to avoid the false positive.

Fixes #5898
@AndriySvyryd AndriySvyryd added closed-fixed The issue has been fixed and is/will be included in the release indicated by the issue milestone. and removed 1 - Working labels Jul 7, 2016
@rowanmiller rowanmiller changed the title KeyAttribute fails when derived entities are included in model Model Builder: Exception when KeyAttribute used with inheritance Jul 21, 2016
@AndriySvyryd AndriySvyryd removed their assignment Aug 18, 2016
@divega divega changed the title Model Builder: Exception when KeyAttribute used with inheritance ModelBuilder: Exception when KeyAttribute used with inheritance Sep 13, 2016
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
closed-fixed The issue has been fixed and is/will be included in the release indicated by the issue milestone. type-bug
Projects
None yet
Development

No branches or pull requests

4 participants