简体   繁体   中英

How do I create composite key which is combination of base class and derived class in Entity Framework Core

I have requirement to create composite key in Entity Framework.

My base class key is "Guid" and there is something unique I want in student class like "ID" which can be readable. like "STUD01", which really requires readable unique data.

[NotMapped]
public class BaseEntity
{
     public Guid Key { get; set; }
     public DateTime? DateCreated { get; set; }
     public string UserCreated { get; set; }
     public DateTime? DateModified { get; set; }
     public string UserModified { get; set; }
}

Here is my Student class

 public class Student : BaseEntity
 {
      public string Id { get; set; }
      public string Name { get; set; }
 }

Here is my context class

public class SchoolContext: DbContext
{       
    public LibraContext(DbContextOptions<SchoolContext> options)
        : base(options)
    { }

    public DbSet<Student> Students { get; set; }
}

 protected override void OnModelCreating(ModelBuilder modelBuilder)
 {
     modelBuilder.Entity<BaseEntity>().Property(x => x.DateCreated).HasDefaultValueSql("GETDATE()");
     modelBuilder.Entity<BaseEntity>().Property(x => x.DateModified).HasDefaultValueSql("GETDATE()");

     //here is my composite key
     modelBuilder.Entity<Student>().HasKey(c => new { c.Key, c.Id });
 }

I have run the below migration commands to create script and update database

Add-Migration -Name "InitialMigration" -Context "SchoolContext"

and I get this error:

A key cannot be configured on 'Student' because it is a derived type. The key must be configured on the root type 'BaseClass'. If you did not intend for 'BaseClass' to be included in the model, ensure that it is not included in a DbSet property on your context, referenced in a configuration call to ModelBuilder, or referenced from a navigation property on a type that is included in the model.

How to achieve it?

I am using ASP.NET Core 2.1

Problem is that [Not Mapped] data annotation is not ignoring the table creation because Fluent API always has the higher priority than the data annotations (attributes). So you can call modelBuilder.Ignore<BaseEntity>(); after the BaseEntity configuration in the OnModelCreating but calling modelBuilder.Ignore<BaseEntity>(); will lose the BaseEntity configurations.

So best solution would be:

Write the configuration for BaseEntity as follows:

public class BaseEntityConfigurations<TEntity> : IEntityTypeConfiguration<TEntity> where TEntity : BaseEntity
{
    public virtual void Configure(EntityTypeBuilder<TEntity> builder)
    {
        builder.Property(x => x.Key).HasDefaultValueSql("NEWID()");
        //CreatedDate 
        builder.Property(x => x.DateCreated).HasDefaultValueSql("GETDATE()");
        //Updated Date
        builder.Property(x => x.DateModified).HasDefaultValueSql("GETDATE()");
    }
}

Then write the configuration for Student as follows:

public class StudentConfigurations : BaseEntityConfigurations<Student>
{
    public override void Configure(EntityTypeBuilder<Student> builder)
    {
        base.Configure(builder); // Must call this

       // composite key
        builder.HasKey(c => new { c.Key, c.Id });
    }
}

Then in the OnModelCreating as follows:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
     base.OnModelCreating(modelBuilder);

     modelBuilder.ApplyConfiguration(new StudentConfigurations());
}

Now everything should working fine!

Note: If you have already a database then it would not work on migration. You have to generate brand new table with initial migration because Entity Framework core cannot change primary key with migration.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM