【问题标题】:Common configurations for entities implementing an interface实现接口的实体的通用配置
【发布时间】:2018-08-09 09:15:40
【问题描述】:

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

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);
}

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

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

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

有没有更好的办法?

【问题讨论】:

  • 扩展方法?仍然需要列出您需要绑定的每个实体,但不需要继续执行 Property.HasDefaultValue。
  • 在实体类中使用自动属性初始化器? public bool IsActive { get; set; } = true; 或将属性名称“反转”为“IsDeleted”之类的名称,以便它自动获取正确的默认值。
  • 拥有具有属性和默认值的抽象类看起来是不错的选择。不知道你为什么不想要它。
  • 那么你在另一个问题中有这个选项,一个基本配置类,它将设置你需要的东西。如果您不想改变整个结构,如何进行配置并引入新的配置类,则可以选择扩展方法,它至少可以部分抽象它。
  • 从 EF6 开始,我会使用 modelBuilder.Types&lt;ISoftDeletable&gt;().Configure(c =&gt; c.Property(e =&gt; e.IsActive).HasDefaultValue(true)) 这样的东西,但是 EF6 没有 HasDefaultValue,我不知道 EF Core 是否仍然支持 Types 配置。跨度>

标签: c# entity-framework-core


【解决方案1】:

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

除了上面的 cmets 之外,还有一种方法可以做到这一点,而无需为每个实体调用它。这可能可以重构为 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&lt;&gt; 类要好得多,因为您不必记住对所有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));
    });
}

【讨论】:

  • 不错。也不知道您可以简单地使用字符串选择属性。 ?
  • @Erndob 是的,如果它在动作参数中使用通用 EntityTypeBuilder 会更干净,但这需要使用反射
  • @Konrad 我尝试使用通用的EntityTypeBuilder,因为它似乎是一个很好的解决方案,但遇到了问题。当我通过反射从ModelBuilderEntity&lt;T&gt;() 方法中提取EntityTypeBuilder&lt;T&gt; 时,我在调用它时收到了这个错误。 System.ArgumentException: The specified type 'IExample' must be a non-interface reference type to be used as an entity type 所以这可能是不可能的。 ://
  • @zxcv 我不记得了。但是我上次做的时候我的扩展方法在接口上工作得很好。不知道 3.0+ 有没有变化
  • ahhh ClrType - 这是我出错的地方,谢谢伙计!!!
【解决方案2】:

我想做与此类似的事情,但使用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);
    }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-06-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-11-18
    相关资源
    最近更新 更多