代码之家  ›  专栏  ›  技术社区  ›  Konrad

实现接口的实体的公共配置

  •  0
  • Konrad  · 技术社区  · 6 年前

    假设我有一些接口,比如:

    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

    有更好的办法吗?

    1 回复  |  直到 6 年前
        1
  •  2
  •   Konrad    6 年前

    我在这里找到了答案: 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<> 上课,因为你不必记得把它全部用上 等位异构体 上课。


    更干净:

    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));
        });
    }
    
        2
  •  0
  •   Codemunkie    5 年前

    我想做一些类似的事情,但是使用 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 :

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        foreach (var entityType in modelBuilder.Model.GetEntityTypes())
        {
            modelBuilder.ApplyConfiguration<IHasDisplayId>(typeof(HasDisplayIdEntityTypeConfiguration<>), entityType.ClrType);
        }
    }