实体实现接口的通用配置

时间:2018-08-09 09:15:40

标签: c# entity-framework-core

假设我有一些界面,例如:

public interface ISoftDeletable
{
    bool IsActive { get; set }
}

我有很多实现它的实体:

public class Entity1 : ISoftDeletable
{
    public int Id { get; set }
    public bool IsActive { get; set; }
}

public class Entity2 : ISoftDeletable
{
    public int Id { get; set }
    public bool IsActive { get; set; }
}

OnModelCreating中:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Entity1>().Property(e => e.IsActive).HasDefaultValue(true);
    modelBuilder.Entity<Entity2>().Property(e => e.IsActive).HasDefaultValue(true);
}

是否有任何方法可以重构它,所以我可以为实现HasDefaultValue的所有实体设置ISoftDeletable而不是像上面那样做?

我可能可以为每个实体使用IsActive = true使用默认构造函数来解决这种特定情况,甚至可以创建一个基本抽象类,但是我不太喜欢。

相似的问题:Ef core fluent api set all column types of interface

还有更好的方法吗?

2 个答案:

答案 0 :(得分:2)

我在这里找到了一些答案:GetEntityTypes: configure entity properties using the generic version of .Property<TEntity> in EF Core

除了上面的注释之外,还有一种方法可以不对每个实体调用它。如我的问题下Erndob的评论所述,这可能可以重构为某种扩展方法。

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    foreach (var entityType in modelBuilder.Model.GetEntityTypes())
    {
        if (typeof(ISoftDeletable).IsAssignableFrom(entityType.ClrType))
        {
            modelBuilder.Entity(entityType.ClrType).Property<bool>(nameof(ISoftDeletable.IsActive)).HasDefaultValue(true);
        }
    }
}

解决方案是使用ModelBuilder.Model.GetEntityTypes()并查找可从ISoftDeletable分配的实体类型。

我认为,这比手动配置它甚至创建抽象IEntityTypeConfiguration<>类要好得多,因为您不必记住对所有ISoftDeletable类都使用它。


外观更简洁

public static class ModelBuilderExtensions
{
    public static ModelBuilder EntitiesOfType<T>(this ModelBuilder modelBuilder,
        Action<EntityTypeBuilder> buildAction) where T : class
    {
        return modelBuilder.EntitiesOfType(typeof(T), buildAction);
    }

    public static ModelBuilder EntitiesOfType(this ModelBuilder modelBuilder, Type type,
        Action<EntityTypeBuilder> buildAction)
    {
        foreach (var entityType in modelBuilder.Model.GetEntityTypes())
            if (type.IsAssignableFrom(entityType.ClrType))
                buildAction(modelBuilder.Entity(entityType.ClrType));

        return modelBuilder;
    }
}

还有OnModelCreating

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.EntitiesOfType<ISoftDeletable>(builder =>
    {
        builder.Property<bool>(nameof(ISoftDeletable.IsActive)).HasDefaultValue(true);

        // query filters :)
        var param = Expression.Parameter(builder.Metadata.ClrType, "p");
        var body = Expression.Equal(Expression.Property(param, nameof(ISoftDeletable.IsActive)), Expression.Constant(true));
        builder.HasQueryFilter(Expression.Lambda(body, param));
    });
}

答案 1 :(得分:0)

我想做类似的事情,但是使用app.json界面保存我的通用配置。我最终不得不使用反射,但是有效:

IEntityTypeConfiguration

Interface

public interface IHasDisplayId { Guid DisplayId { get; } }

EntityTypeConfig

public class HasDisplayIdEntityTypeConfiguration<T> : IEntityTypeConfiguration<T> where T : class, IHasDisplayId { public void Configure(EntityTypeBuilder<T> builder) { builder.Property(e => e.DisplayId).IsRequired(); builder.HasIndex(e => e.DisplayId); } }

Extension method

public static ModelBuilder ApplyConfiguration<T>(this ModelBuilder modelBuilder, Type configurationType, Type entityType) { if (typeof(T).IsAssignableFrom(entityType)) { // Build IEntityTypeConfiguration type with generic type parameter var configurationGenericType = configurationType.MakeGenericType(entityType); // Create an instance of the IEntityTypeConfiguration implementation var configuration = Activator.CreateInstance(configurationGenericType); // Get the ApplyConfiguration method of ModelBuilder via reflection var applyEntityConfigurationMethod = typeof(ModelBuilder) .GetMethods() .Single(e => e.Name == nameof(ModelBuilder.ApplyConfiguration) && e.ContainsGenericParameters && e.GetParameters().SingleOrDefault()?.ParameterType.GetGenericTypeDefinition() == typeof(IEntityTypeConfiguration<>)); // Create a generic ApplyConfiguration method with our entity type var target = applyEntityConfigurationMethod.MakeGenericMethod(entityType); // Invoke ApplyConfiguration, passing our IEntityTypeConfiguration instance target.Invoke(modelBuilder, new[] { configuration }); } return modelBuilder; }

Usage