将 IEntityTypeConfiguration 与基本实体一起使用
Use IEntityTypeConfiguration with a base entity
在 EF Core 2.0 中,我们能够从 IEntityTypeConfiguration
派生更清晰的 Fluent API 映射 (source)。
如何扩展此模式以利用基础实体?在下面的示例中,我如何使用 BaseEntityConfiguration
来减少 LanguageConfiguration
和 MaintainerConfiguration
中的重复,修改 BaseEntity
中的属性仅在 BaseEntityConfiguration
中?这样的 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");
}
}
这样的东西可以工作(未经测试)?
public abstract class BaseEntityTypeConfiguration<TBase> : IEntityTypeConfiguration<TBase>
where TBase : BaseEntity
{
public virtual void Configure(EntityTypeBuilder<TBase> entityTypeBuilder)
{
//Base Configuration
}
}
public class MaintainerConfiguration : BaseEntityTypeConfiguration<Maintainer>
{
public override void Configure(EntityTypeBuilder<Maintainer> entityTypeBuilder)
{
entityTypeBuilder.Property(b => b.CreatedDateUtc).HasDefaultValueSql("CURRENT_TIMESTAMP");
base.Configure(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 ");
// …
}
就是找到所有继承自基础Entity的Entites,循环遍历它们并调用如下所示的通用方法,其中放置了冗余逻辑:
protected override void OnModelCreating(ModelBuilder modelBuilder){
foreach (Type type in GetEntityTypes(typeof(BaseEntity))){
var method = SetGlobalQueryMethod.MakeGenericMethod(type);
method.Invoke(this, new object[] { modelBuilder });
}
}
static readonly MethodInfo SetGlobalQueryMethod = typeof(/*your*/Context)
.GetMethods(BindingFlags.Public | BindingFlags.Instance)
.Single(t => t.IsGenericMethod && t.Name == "SetGlobalQuery");
public void SetGlobalQuery<T>(ModelBuilder builder) where T : BaseEntity{
builder.Entity<T>().Property(o => o.CreatedDateTime).HasDefaultValueSql("CURRENT_TIMESTAMP");
// Additional Statements
}
对于 "GetEntityTypes" 方法,您需要 Nuget 包 „Microsoft.Extensions.DependencyModel“
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");
}
}
通过这种方式,您无需在子配置中编写任何一行。
我来晚了,但这是我在 OnModelCreating 方法中为获得类似结果所做的。
基本上,我有 (4) 个从 BaseEntity 继承的属性。其中两个是日期,为什么两个是字符串。
对于日期,我希望默认值为 SQL 的 GETUTCDATE,字符串为 "SystemGenerated." 使用允许我从中检索 属性 名称的静态助手BaseEntity 以强类型方式获取 (4) 属性 个名称。然后,在我的主要映射设置完成后,我遍历所有 ModelBuilder 实体。这允许 modelBuilder.Model.GetEntityTypes 到 return modelBuidler 知道的实体。然后就是查看 ClrType.BaseType 以查看类型是否继承自我的 BaseEntity 并在 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");
}
}
这是获取给定类型的 属性 名称的静态助手..
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;
}
}
在 EF Core 2.0 中,我们能够从 IEntityTypeConfiguration
派生更清晰的 Fluent API 映射 (source)。
如何扩展此模式以利用基础实体?在下面的示例中,我如何使用 BaseEntityConfiguration
来减少 LanguageConfiguration
和 MaintainerConfiguration
中的重复,修改 BaseEntity
中的属性仅在 BaseEntityConfiguration
中?这样的 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");
}
}
这样的东西可以工作(未经测试)?
public abstract class BaseEntityTypeConfiguration<TBase> : IEntityTypeConfiguration<TBase>
where TBase : BaseEntity
{
public virtual void Configure(EntityTypeBuilder<TBase> entityTypeBuilder)
{
//Base Configuration
}
}
public class MaintainerConfiguration : BaseEntityTypeConfiguration<Maintainer>
{
public override void Configure(EntityTypeBuilder<Maintainer> entityTypeBuilder)
{
entityTypeBuilder.Property(b => b.CreatedDateUtc).HasDefaultValueSql("CURRENT_TIMESTAMP");
base.Configure(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 ");
// …
}
就是找到所有继承自基础Entity的Entites,循环遍历它们并调用如下所示的通用方法,其中放置了冗余逻辑:
protected override void OnModelCreating(ModelBuilder modelBuilder){
foreach (Type type in GetEntityTypes(typeof(BaseEntity))){
var method = SetGlobalQueryMethod.MakeGenericMethod(type);
method.Invoke(this, new object[] { modelBuilder });
}
}
static readonly MethodInfo SetGlobalQueryMethod = typeof(/*your*/Context)
.GetMethods(BindingFlags.Public | BindingFlags.Instance)
.Single(t => t.IsGenericMethod && t.Name == "SetGlobalQuery");
public void SetGlobalQuery<T>(ModelBuilder builder) where T : BaseEntity{
builder.Entity<T>().Property(o => o.CreatedDateTime).HasDefaultValueSql("CURRENT_TIMESTAMP");
// Additional Statements
}
对于 "GetEntityTypes" 方法,您需要 Nuget 包 „Microsoft.Extensions.DependencyModel“
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");
}
}
通过这种方式,您无需在子配置中编写任何一行。
我来晚了,但这是我在 OnModelCreating 方法中为获得类似结果所做的。
基本上,我有 (4) 个从 BaseEntity 继承的属性。其中两个是日期,为什么两个是字符串。
对于日期,我希望默认值为 SQL 的 GETUTCDATE,字符串为 "SystemGenerated." 使用允许我从中检索 属性 名称的静态助手BaseEntity 以强类型方式获取 (4) 属性 个名称。然后,在我的主要映射设置完成后,我遍历所有 ModelBuilder 实体。这允许 modelBuilder.Model.GetEntityTypes 到 return modelBuidler 知道的实体。然后就是查看 ClrType.BaseType 以查看类型是否继承自我的 BaseEntity 并在 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");
}
}
这是获取给定类型的 属性 名称的静态助手..
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;
}
}