CrazyBaran
CrazyBaran

Reputation: 592

EF: Map base class property with fluent API

I have problem with inheritance code first. I have base class which look like this:

public abstract class BaseEntity
{
    public DateTime? DateCreated { get; set; }
    public string UserCreatedId { get; set; }
    public DateTime? DateModified { get; set; }
    public string UserModifiedId { get; set; }
    public virtual UserState UserCreated { get; set; }
    public virtual UserState UserModified { get; set; }
}

and i have Inherited it and this give me a additional column with name UserCreated_userStateId and UserModified_UserStateId. So I try fluent API like this:

modelBuilder.Entity<BaseEntity>().HasOptional(x => x.UserCreated).WithMany().HasForeignKey(x => x.UserCreatedId);
modelBuilder.Entity<BaseEntity>().HasOptional(X => X.UserModified).WithMany().HasForeignKey(x => x.UserModifiedId);

But this give me error: "One or more validation errors were detected during model generation:

BaseEntity: : EntityType 'BaseEntity' has no key defined. Define the key for this EntityType. BaseEntities: EntityType: EntitySet 'BaseEntities' is based on type 'BaseEntity' that has no keys defined." How to avoid define the key and only change mapping of property? I have key in derived class.

Upvotes: 2

Views: 1678

Answers (1)

grek40
grek40

Reputation: 13458

You can wrap your base class configuration in a generic method and call it for each concrete entity type.

static void ConfigureBaseEntity<TDerived>(EntityTypeConfiguration<TDerived> entityTypeConfiguration) where TDerived : BaseEntity
{
    // your base class configuration
    entityTypeConfiguration.HasOptional(x => x.UserCreated).WithMany().HasForeignKey(x => x.UserCreatedId);
    entityTypeConfiguration.HasOptional(X => X.UserModified).WithMany().HasForeignKey(x => x.UserModifiedId);
}

Call for each concrete entity type

public class DerivedEntity : BaseEntity
{
    public int Id { get; set; }
}

//...
ConfigureBaseEntity(modelBuilder.Entity<DerivedEntity>());

Edit

Maybe the Types configuration is enough for your purpose.

modelBuilder.Types<BaseEntity>().Configure(entityTypeConfiguration => /* configure using entityTypeConfiguration  */);

Otherwise, as commented, you may need reflection. For example, lets say all your derived classes are in the same assembly as the BaseEntity class and the ConfigureBaseEntity method from above is in class BaseEntityConfiguration.

var entityMethod = typeof(DbModelBuilder).GetMethod("Entity");
var configurationMethod = typeof(BaseEntityConfiguration).GetMethod("ConfigureBaseEntity");
foreach (Type t in typeof(BaseEntity).Assembly.GetTypes().Where(x => x.IsSubclassOf(typeof(BaseEntity))))
{
    var configurator = entityMethod.MakeGenericMethod(t).Invoke(modelBuilder, new object[0]);
    configurationMethod.MakeGenericMethod(t).Invoke(null, new object[1] { configurator });
}

However, if it was my code, I'd prefer to write one line of code per entity class instead of going for the dynamic discovery.

Upvotes: 2

Related Questions