C# 将IEntityTypeConfiguration与基本实体一起使用

C# 将IEntityTypeConfiguration与基本实体一起使用,c#,asp.net-core,entity-framework-core,ef-fluent-api,ef-core-2.0,C#,Asp.net Core,Entity Framework Core,Ef Fluent Api,Ef Core 2.0,在EF Core 2.0中,我们能够从IEntityTypeConfiguration派生出更干净的Fluent API映射() 如何扩展此模式以利用基本实体?在下面的示例中,如何使用BaseEntityConfiguration来减少LanguageConfiguration和MaintainerConfiguration中的重复,只修改BaseEntityConfiguration中的BaseEntity中的属性?这样的BaseEntityConfiguration会是什么样子;如果在OnM

在EF Core 2.0中,我们能够从
IEntityTypeConfiguration
派生出更干净的Fluent API映射()

如何扩展此模式以利用基本实体?在下面的示例中,如何使用
BaseEntityConfiguration
来减少
LanguageConfiguration
MaintainerConfiguration
中的重复,只修改
BaseEntityConfiguration
中的
BaseEntity
中的属性?这样的
BaseEntityConfiguration
会是什么样子;如果在
OnModelCreating()
中使用,它将如何使用?请参见示例末尾代码中的TODO

例如:

public abstract class BaseEntity
{
    public long Id { get; set; }
    public DateTime CreatedDateUtc { get; set; }
    public DateTime? ModifiedDateUtc { get; set; }
}

public class Language : BaseEntity
{
    public string Iso6392 { get; set; }
    public string LocalName { get; set; }
    public string Name { get; set; }
}

public class Maintainer : BaseEntity
{
    public string Email { get; set; }
    public string Name { get; set; }
}

public class FilterListsDbContext : DbContext
{
    public FilterListsDbContext(DbContextOptions options) : base(options)
    {
    }

    public DbSet<Language> Languages { get; set; }
    public DbSet<Maintainer> Maintainers { get; set; }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        //TODO: Possibly add something like BaseEntityConfiguration?
        modelBuilder.ApplyConfiguration(new LanguageConfiguration());
        modelBuilder.ApplyConfiguration(new MaintainerConfiguration());
    }
}

public class LanguageConfiguration : IEntityTypeConfiguration<Language>
{
    public void Configure(EntityTypeBuilder<Language> entityTypeBuilder)
    {
        //TODO: Move this to something like BaseEntityConfiguration?
        entityTypeBuilder.Property(b => b.CreatedDateUtc).HasDefaultValueSql("CURRENT_TIMESTAMP");
    }
}

public class MaintainerConfiguration : IEntityTypeConfiguration<Maintainer>
{
    public void Configure(EntityTypeBuilder<Maintainer> entityTypeBuilder)
    {
        //TODO: Move this to something like BaseEntityConfiguration?
        entityTypeBuilder.Property(b => b.CreatedDateUtc).HasDefaultValueSql("CURRENT_TIMESTAMP");
    }
}
公共抽象类BaseEntity
{
公共长Id{get;set;}
public DateTime CreatedDateUtc{get;set;}
公共日期时间?修改日期{get;set;}
}
公共类语言:BaseEntity
{
公共字符串Iso6392{get;set;}
公共字符串LocalName{get;set;}
公共字符串名称{get;set;}
}
公共类维护者:BaseEntity
{
公共字符串电子邮件{get;set;}
公共字符串名称{get;set;}
}
公共类筛选器ListSdbContext:DbContext
{
公共过滤器ListSBContext(DbContextOptions选项):基本(选项)
{
}
公共数据库集语言{get;set;}
公共数据库集维护程序{get;set;}
模型创建时受保护的覆盖无效(ModelBuilder ModelBuilder)
{
//TODO:是否可能添加BaseEntityConfiguration之类的内容?
ApplyConfiguration(新语言配置());
modelBuilder.ApplyConfiguration(新的MaintainerConfiguration());
}
}
公共类语言配置:IEntityTypeConfiguration
{
公共void配置(EntityTypeBuilder EntityTypeBuilder)
{
//TODO:将其移动到类似BaseEntityConfiguration的位置?
属性(b=>b.createdDataeTc).HasDefaultValueSql(“当前_时间戳”);
}
}
公共类维护器配置:IEntityTypeConfiguration
{
公共void配置(EntityTypeBuilder EntityTypeBuilder)
{
//TODO:将其移动到类似BaseEntityConfiguration的位置?
属性(b=>b.createdDataeTc).HasDefaultValueSql(“当前_时间戳”);
}
}

类似的东西可以工作(未经测试)

公共抽象类BaseEntityTypeConfiguration:EntityTypeConfiguration
其中TBase:BaseEntity
{
公共虚拟void配置(EntityTypeBuilder EntityTypeBuilder)
{
//基本配置
}
}
公共类维护器配置:BaseEntityTypeConfiguration
{
公共覆盖无效配置(EntityTypeBuilder EntityTypeBuilder)
{
属性(b=>b.createdDataeTc).HasDefaultValueSql(“当前_时间戳”);
配置(entityTypeBuilder);
}
}

如果您不想重复继承自同一基本实体的所有模型的列定义,另一种方法如下:

protected override void OnModelCreating(ModelBuilder modelBuilder){
        modelBuilder.Entity<Order>()
            .Property(b => b.CreatedDateTime)
            .HasDefaultValueSql("CURRENT_TIMESTAMP ");

        modelBuilder.Entity<Adress>()
            .Property(b => b.CreatedDateTime)
            .HasDefaultValueSql("CURRENT_TIMESTAMP ");
        // …

}
模型创建时受保护的覆盖无效(ModelBuilder ModelBuilder){
modelBuilder.Entity()
.Property(b=>b.CreatedDateTime)
.HasDefaultValueSql(“当前_时间戳”);

modelBuilder.Entity

还有另一种解决问题的方法,那就是使用模板方法设计模式。如下所示:

public abstract class BaseEntityTypeConfiguration<TBase> : IEntityTypeConfiguration<TBase>
    where TBase : BaseEntity
{
    public void Configure(EntityTypeBuilder<TBase> entityTypeBuilder)
    {
        //Base Configuration

        ConfigureOtherProperties(builder);
    }

    public abstract void ConfigureOtherProperties(EntityTypeBuilder<TEntity> builder);
}

public class MaintainerConfiguration : BaseEntityTypeConfiguration<Maintainer>
{
    public override void ConfigureOtherProperties(EntityTypeBuilder<Maintainer> entityTypeBuilder)
    {
        entityTypeBuilder.Property(b => b.CreatedDateUtc).HasDefaultValueSql("CURRENT_TIMESTAMP");        
    }
}
公共抽象类BaseEntityTypeConfiguration:EntityTypeConfiguration
其中TBase:BaseEntity
{
公共void配置(EntityTypeBuilder EntityTypeBuilder)
{
//基本配置
配置其他属性(生成器);
}
公共抽象void配置其他属性(EntityTypeBuilder);
}
公共类维护器配置:BaseEntityTypeConfiguration
{
公共覆盖无效配置其他属性(EntityTypeBuilder EntityTypeBuilder)
{
属性(b=>b.createdDataeTc).HasDefaultValueSql(“当前_时间戳”);
}
}

通过这种方式,您不需要在子配置中编写任何一行。

我迟到了,但这就是我在OnModelCreating方法中所做的,以获得类似的结果

基本上,我有(4)个从BaseEntity继承的属性,其中两个是日期,两个是字符串

对于日期,我希望默认值为SQL的GETUTCDATE,字符串为“SystemGenerated”。使用静态帮助程序,允许我以强类型方式从BaseEntity检索属性名称,我获取(4)属性名称。然后,在设置主映射后,我迭代所有的ModelBuilder实体。这允许ModelBuilder.Model.GetEntityTypes返回ModelBuilder知道的实体。然后,只需查看ClrType.BaseType,查看该类型是否继承自我的BaseEntity和set正在清除PropertyBuilder上的默认值

我直接测试了这一点,并通过EF迁移确认生成了正确的SQL

var createdAtUtc = StaticHelpers.GetPropertyName<BaseEntity>(x => x.CreatedAtUtc);
var lastModifiedAtUtc = StaticHelpers.GetPropertyName<BaseEntity>(x => x.LastModifiedAtUtc);
var createdBy = StaticHelpers.GetPropertyName<BaseEntity>(x => x.CreatedBy);
var lastModifiedBy = StaticHelpers.GetPropertyName<BaseEntity>(x => x.LastModifiedBy);
foreach (var t in modelBuilder.Model.GetEntityTypes())
{
    if (t.ClrType.BaseType == typeof(BaseEntity))
    {
        modelBuilder.Entity(t.ClrType).Property(createdAtUtc).HasDefaultValueSql("GETUTCDATE()");
        modelBuilder.Entity(t.ClrType).Property(lastModifiedAtUtc).HasDefaultValueSql("GETUTCDATE()");
        modelBuilder.Entity(t.ClrType).Property(createdBy).HasDefaultValueSql("SystemGenerated");
        modelBuilder.Entity(t.ClrType).Property(lastModifiedBy).HasDefaultValueSql("SystemGenerated");
    }
}
var createdAtUtc=StaticHelpers.GetPropertyName(x=>x.createdAtUtc);
var lastModifiedAtUtc=StaticHelpers.GetPropertyName(x=>x.lastModifiedAtUtc);
var createdBy=StaticHelpers.GetPropertyName(x=>x.createdBy);
var lastModifiedBy=StaticHelpers.GetPropertyName(x=>x.lastModifiedBy);
foreach(modelBuilder.Model.GetEntityTypes()中的var t)
{
if(t.ClrType.BaseType==typeof(BaseEntity))
{
modelBuilder.Entity(t.ClrType).Property(createdAtUtc).HasDefaultValueSql(“GETUTCDATE()”);
modelBuilder.Entity(t.ClrType).Property(lastModifiedAtUtc).HasDefaultValueSql(“GETUTCDATE()”);
modelBuilder.Entity(t.ClrType).Property(createdBy).HasDefaultValueSql(“SystemGenerated”);
modelBuilder.Entity(t.ClrType).Property(lastModifiedBy).HasDefaultValueSql(“SystemGenerated”);
}
}
下面是获取给定类型的属性名的静态帮助程序。

private static IList<Type> _entityTypeCache; private static IList<Type> GetEntityTypes(Type type) { if (_entityTypeCache != null && _entityTypeCache.First().BaseType == type) { return _entityTypeCache.ToList(); } _entityTypeCache = (from a in GetReferencingAssemblies() from t in a.DefinedTypes where t.BaseType == type select t.AsType()).ToList(); return _entityTypeCache; } private static IEnumerable<Assembly> GetReferencingAssemblies() { var assemblies = new List<Assembly>(); var dependencies = DependencyContext.Default.RuntimeLibraries; foreach (var library in dependencies) { try { var assembly = Assembly.Load(new AssemblyName(library.Name)); assemblies.Add(assembly); } catch (FileNotFoundException) { } } return assemblies; }
public abstract class BaseEntityTypeConfiguration<TBase> : IEntityTypeConfiguration<TBase>
    where TBase : BaseEntity
{
    public void Configure(EntityTypeBuilder<TBase> entityTypeBuilder)
    {
        //Base Configuration

        ConfigureOtherProperties(builder);
    }

    public abstract void ConfigureOtherProperties(EntityTypeBuilder<TEntity> builder);
}

public class MaintainerConfiguration : BaseEntityTypeConfiguration<Maintainer>
{
    public override void ConfigureOtherProperties(EntityTypeBuilder<Maintainer> entityTypeBuilder)
    {
        entityTypeBuilder.Property(b => b.CreatedDateUtc).HasDefaultValueSql("CURRENT_TIMESTAMP");        
    }
}
var createdAtUtc = StaticHelpers.GetPropertyName<BaseEntity>(x => x.CreatedAtUtc);
var lastModifiedAtUtc = StaticHelpers.GetPropertyName<BaseEntity>(x => x.LastModifiedAtUtc);
var createdBy = StaticHelpers.GetPropertyName<BaseEntity>(x => x.CreatedBy);
var lastModifiedBy = StaticHelpers.GetPropertyName<BaseEntity>(x => x.LastModifiedBy);
foreach (var t in modelBuilder.Model.GetEntityTypes())
{
    if (t.ClrType.BaseType == typeof(BaseEntity))
    {
        modelBuilder.Entity(t.ClrType).Property(createdAtUtc).HasDefaultValueSql("GETUTCDATE()");
        modelBuilder.Entity(t.ClrType).Property(lastModifiedAtUtc).HasDefaultValueSql("GETUTCDATE()");
        modelBuilder.Entity(t.ClrType).Property(createdBy).HasDefaultValueSql("SystemGenerated");
        modelBuilder.Entity(t.ClrType).Property(lastModifiedBy).HasDefaultValueSql("SystemGenerated");
    }
}
public static string GetPropertyName<T>(Expression<Func<T, object>> expression)
{
    if (expression.Body is MemberExpression)
    {
        return ((MemberExpression)expression.Body).Member.Name;
    }
    else
    {
        var op = ((UnaryExpression)expression.Body).Operand;
        return ((MemberExpression)op).Member.Name;
    }
}