使用 fluent 在某种类型的实体上配置 EF Core 属性

Use fluent to configure EF Core properties on entities of a certain type

我有一堆实体,我想要字段 InsertDateUpdateDate,(SQL) 默认值为 GETDATE().

为此,我做了一个简单的 class AuditableEntity,我的一堆实体从中派生出来的基本上是这样的:

public class AuditableEntity
{
    InsertDate { get; set; }
    UpdateDate { get; set; }
}

和示例实体

public class Customer : AuditableEntity
{
    CustomerId int { get; set; }
    // etc..
}

然后当我使用 fluent API 配置实体时,我可以做类似的事情:

public class CustomerConfiguration : IEntityTypeConfiguration<Customer>
{
    public void Configure(EntityTypeBuilder<Customer> builder)
    {
        builder.Property(e => e.InsertDate)
            .HasDefaultValueSql("getdate()");
        builder.Property(e => e.UpdateDate)
            .HasDefaultValueSql("getdate()");
    }
}

到目前为止,还不错。

除了我有一堆这样的 AuditableEntities,每个都需要设置相同的两个 属性 定义。

是否有一些中心位置可以为所有 AuditableEntity 的子类型的实体构建属性?我尝试了类似下面的方法,但它失败了,因为它看起来像是在尝试实例化一个 DbSet<AuditableEntity> 而我想要做的就是向所有 AuditableEntity 类型的实体添加一个相同的 属性 .

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<AuditableEntity>(entity =>
        {
            entity.Property(e => e.InsertDate)
                .HasDefaultValueSql("getdate()");
            entity.Property(e => e.UpdateDate)
                .HasDefaultValueSql("getdate()");
        });
    modelBuilder.ApplyConfigurationsFromAssembly(typeof(MyContext).Assembly);
    }
    /* When I try to run add-migration with this in here, it says:
     * system.InvalidOperationException: A key cannot be configured on 'DatasetSource' because it is a derived type. The key must be configured on the root type 'AuditableEntity'. If you did not intend for 'AuditableEntity' to be included in the model, ensure that it is not referenced by a DbSet property on your context, referenced in a configuration call to ModelBuilder, or referenced from a navigation on a type that is included in the model.
     */

选项 1;

流利的API帮你配置每一个IMutableEntity。但你可以自己做;

foreach (var table in modelBuilder.Model.GetEntityTypes()){
    if (table.ClrType.IsAssignableTo(typeof(AuditableEntity))){
        foreach(var p in table.GetProperties()
            .Where(p => p.Name == nameof(AuditableEntity.InsertDate) || p.Name == nameof(AuditableEntity.UpdateDate)))
            p.SetDefaultValueSql("getdate()");
    }
}

您可以通过阅读 source code on github.

了解其他流畅的 api 调用实际上做了什么

选项 2;

编写通用方法并从每个 IEntityTypeConfiguration 实现中手动调用它。

public void ConfigureAuditing<T>(EntityTypeBuilder<T> builder)
    where T:AuditableEntity
{
    builder.Property(e => e.InsertDate)
        .HasDefaultValueSql("getdate()");
    builder.Property(e => e.UpdateDate)
        .HasDefaultValueSql("getdate()");
}

public void Configure(EntityTypeBuilder<Customer> builder){
    ConfigureAuditing(builder);
}

选项 3;

将这两种方法结合起来。使用反射和泛型方法来调用流利的 API;

public void ConfigureAuditing<T>(ModelBuilder modelBuilder)
    where T:AuditableEntity{
    var builder = modelBuilder.Entity<T>();
    // as above
}

var method = new Action<ModelBuilder>(ConfigureAuditing<AuditableEntity>)
    .Method
    .GetGenericMethodDefinition();

foreach (var table in modelBuilder.Model.GetEntityTypes()){
    if (table.ClrType.IsAssignableTo(typeof(AuditableEntity))){
        method.MakeGenericMethod(table.ClrType)
            .Invoke(null, new object[] { modelBuilder });
    }
}