With the lack of custom conventions, you could use the typical modelBuilder.Model.GetEntityTypes()
loop, identify the target entity types and invoke common configuration.
Identification in your case is a bit complicated because of the base generic class, but doable by iterating down Type.BaseType
and check for BaseEntity<>
. Once you find it, you can retrieve the generic argument T
which you'll need later.
If you don't want to use generic class implementing IEnityTypeConfiguration<TEntity>
, then the idea is to put the implementation in generic constrained method like this
static void Configure<TEntity, T>(ModelBuilder modelBuilder)
where TEntity : BaseEntity<T>
{
modelBuilder.Entity<TEntity>(builder =>
{
builder.Property(e => e.OrderId).ValueGeneratedOnAdd();
});
}
Passing the actual entity type TEntity
to modelBuilder.Enity
method is crucial, because otherwise EF Core will consider whatever you pass to be an entity type and configure TPH inheritance.
Calling the method requires reflection - finding the generic method definition, using MakeGenericMethod
and then Invoke
.
Here is all that encapsulated in a static class:
public static class BaseEntityConfiguration
{
static void Configure<TEntity, T>(ModelBuilder modelBuilder)
where TEntity : BaseEntity<T>
{
modelBuilder.Entity<TEntity>(builder =>
{
builder.Property(e => e.OrderId).ValueGeneratedOnAdd();
});
}
public static ModelBuilder ApplyBaseEntityConfiguration(this ModelBuilder modelBuilder)
{
var method = typeof(BaseEntityConfiguration).GetTypeInfo().DeclaredMethods
.Single(m => m.Name == nameof(Configure));
foreach (var entityType in modelBuilder.Model.GetEntityTypes())
{
if (entityType.ClrType.IsBaseEntity(out var T))
method.MakeGenericMethod(entityType.ClrType, T).Invoke(null, new[] { modelBuilder });
}
return modelBuilder;
}
static bool IsBaseEntity(this Type type, out Type T)
{
for (var baseType = type.BaseType; baseType != null; baseType = baseType.BaseType)
{
if (baseType.IsGenericType && baseType.GetGenericTypeDefinition() == typeof(BaseEntity<>))
{
T = baseType.GetGenericArguments()[0];
return true;
}
}
T = null;
return false;
}
}
Now all you need is to call it from inside your OnModelCreating
override:
modelBuilder.ApplyBaseEntityConfiguration();
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…