Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

EF Core Fluent API Configurations with Inheritance

EF CORE Fluent Api Configuration in separate files are Working fine with simple classes Ref #1 && Ref # 2. The problem comes when entities are Inherited from KeyedEntity or AuditableEntity

class abstract KeyedEntity<TValue> {
      public TValue Id {get; set;}
}

class abstract  AuditableEntity<TValue> : KeyedEntityBase<TValue>{
      public DateTime DateCreated {get; set;}
      public DateTime DateModified {get; set;}
}

Mapper Goes Something like this

public class KeyedEntityMap<TEntity, TId>
    : IEntityTypeConfiguration<TEntity> where TEntity
    : KeyedEntityBase<TId> where TId : struct
{
    public void Configure(EntityTypeBuilder<TEntity> builder)
    {
        // Primary Key
        builder.HasKey(t => t.Id);

        // Properties
        builder.Property(t => t.Id).HasColumnName("id").ValueGeneratedOnAdd();
    }
}

public class AuditableEntityMap<TEntity, TId>
    : IEntityTypeConfiguration<TEntity>    where TEntity 
    : AuditableEntity<TId>    where TId : struct
{
    public void Configure(EntityTypeBuilder<TEntity> builder)
    {
        // Properties
        builder.Property(t => t.DateCreated).HasColumnName("DateCreated");
        builder.Property(t => t.DateModified).HasColumnName("DateModified");           
    }
}

Now the Problem Occurs with the Entity that inherits from AuditableEntity. I need to register Map from that Particular Enitity class along with AuditableEntityMap class and KeyedEntityMap class.

Now I can either forget about Map Inheritance and merge all the complex inheritance Maps in the entity class, which I don't want to do and respect DRY . The problem with complex inheritance is its not registering my entity maps

like image 516
JB's Avatar asked Aug 25 '18 14:08

JB's


People also ask

Does EF core support Fluent API?

EF Fluent API is based on a Fluent API design pattern (a.k.a Fluent Interface) where the result is formulated by method chaining. In Entity Framework Core, the ModelBuilder class acts as a Fluent API.

Which inheritance hierarchy is support in EF core?

TPC and TPH inheritance patterns generally deliver better performance than TPT inheritance patterns, because TPT patterns can result in complex join queries. This tutorial demonstrates how to implement TPH inheritance. TPH is the only inheritance pattern that the Entity Framework Core supports.

What is Fluent API configuration?

Fluent API is an advanced way of specifying model configuration that covers everything that data annotations can do in addition to some more advanced configuration not possible with data annotations.

How do you configure a primary key for an entity in Entity Framework Fluent API?

Configuring a primary key By convention, a property named Id or <type name>Id will be configured as the primary key of an entity. Owned entity types use different rules to define keys. You can configure a single property to be the primary key of an entity as follows: Data Annotations.


1 Answers

There are several ways you can achieve DRY for base entity configuration.

Bit the closest to your current design is to simply follow the entity hierarchy in the configuration classes:

public class KeyedEntityMap<TEntity, TId> : IEntityTypeConfiguration<TEntity>
    where TEntity : KeyedEntityBase<TId>
    where TId : struct
{
    public virtual void Configure(EntityTypeBuilder<TEntity> builder)
    //       ^^^
    {
        // Primary Key
        builder.HasKey(t => t.Id);

        // Properties
        builder.Property(t => t.Id).HasColumnName("id").ValueGeneratedOnAdd();
    }
}

public class AuditableEntityMap<TEntity, TId> : KeyedEntityMap<TEntity, TId>
    //                                                 ^^^
    where TEntity : AuditableEntity<TId>
    where TId : struct
{
    public override void Configure(EntityTypeBuilder<TEntity> builder)
    //       ^^^
    {
        base.Configure(builder); // <<<
        // Properties
        builder.Property(t => t.DateCreated).HasColumnName("DateCreated");
        builder.Property(t => t.DateModified).HasColumnName("DateModified");           
    }
}

and then for specific entity that needs additional configuration:

public class Person : AuditableEntity<int>    
{
    public string Name { get; set; }
}

you would register

public class PersonEntityMap : AuditableEntityMap<Person, int>
{
    public override void Configure(EntityTypeBuilder<Person> builder)
    {
        base.Configure(builder);
        // Properties
        builder.Property(t => t.Name).IsRequired();
        // etc...
    }
}
like image 187
Ivan Stoev Avatar answered Sep 27 '22 20:09

Ivan Stoev