【问题标题】:EF Core: Soft delete with shadow properties and query filtersEF Core:带有阴影属性和查询过滤器的软删除
【发布时间】:2018-05-20 06:56:20
【问题描述】:

我创建了一个界面来尝试进行软删除,混合阴影属性和查询过滤器。但它不起作用。

public interface IDeletableEntity {}

然后在我的模型构建器中

 builder.Model.GetEntityTypes()
                .Where(entityType => typeof(IDeletableEntity).IsAssignableFrom(entityType.ClrType))
                .ToList()
                .ForEach(entityType =>
                {
                    builder.Entity(entityType.ClrType).Property<Boolean>("IsDeleted");
                    builder.Entity(entityType.ClrType).HasQueryFilter(e => EF.Property<Boolean>(e, "IsDeleted") == false);
                });

但带有查询过滤器的行无法编译。我得到的错误是“无法将 lambda 表达式转换为类型 'lambda 表达式',因为它不是委托类型”

如果我这样做,它会起作用。

builder.Entity<MyEntity>().HasQueryFilter(m => EF.Property<Boolean>(m, "IsDeleted") == false);

有什么方法可以做到这一点?这是为了在我想使用软删除实体的每个实体中都有一个带有 IDeletableEntity 的接口而不必这样做

非常感谢,

【问题讨论】:

    标签: c# entity-framework ef-core-2.0


    【解决方案1】:

    非通用EntityTypeBuilderHasQueryFilter(相对于通用EntityTypeBuilder&lt;TEntity&gt;)几乎无法使用,因为没有简单的方法可以创建预期的LambdaExpression

    一种解决方案是使用 Expression 类方法手动构建 lambda 表达式:

    .ForEach(entityType =>
    {
        builder.Entity(entityType.ClrType).Property<Boolean>("IsDeleted");
        var parameter = Expression.Parameter(entityType.ClrType, "e");
        var body = Expression.Equal(
            Expression.Call(typeof(EF), nameof(EF.Property), new[] { typeof(bool) }, parameter, Expression.Constant("IsDeleted")),
        Expression.Constant(false));
        builder.Entity(entityType.ClrType).HasQueryFilter(Expression.Lambda(body, parameter));
    });
    

    另一种是使用原型表达式

    Expression<Func<object, bool>> filter = 
        e => EF.Property<bool>(e, "IsDeleted") == false;
    

    并使用参数替换器将参数与实际类型绑定:

    .ForEach(entityType =>
    {
        builder.Entity(entityType.ClrType).Property<Boolean>("IsDeleted");
        var parameter = Expression.Parameter(entityType.ClrType, "e");
        var body = filter.Body.ReplaceParameter(filter.Parameters[0], parameter);
        builder.Entity(entityType.ClrType).HasQueryFilter(Expression.Lambda(body, parameter));
    });
    

    ReplaceParameter 是我用于表达式树操作的自定义助手扩展方法之一:

    public static partial class ExpressionUtils
    {
        public static Expression ReplaceParameter(this Expression expr, ParameterExpression source, Expression target) =>
            new ParameterReplacer { Source = source, Target = target }.Visit(expr);
    
        class ParameterReplacer : System.Linq.Expressions.ExpressionVisitor
        {
            public ParameterExpression Source;
            public Expression Target;
            protected override Expression VisitParameter(ParameterExpression node) => node == Source ? Target : node;
        }
    }
    

    但我认为最自然的解决方案是将配置代码移动到通用方法中并通过反射调用它。例如:

    static void ConfigureSoftDelete<T>(ModelBuilder builder)
        where T : class, IDeletableEntity
    {
        builder.Entity<T>().Property<Boolean>("IsDeleted");
        builder.Entity<T>().HasQueryFilter(e => EF.Property<bool>(e, "IsDeleted") == false);
    }
    

    然后

    .ForEach(entityType => GetType()
        .GetMethod(nameof(ConfigureSoftDelete), BindingFlags.NonPublic | BindingFlags.Static)
        .MakeGenericMethod(entityType.ClrType)
        .Invoke(null, new object[] { builder })
    );
    

    【讨论】:

    • 谢谢,你在这里为我省去了很多麻烦
    • 第二个区块中的filter 是什么
    • @deadManN 好问题,好像我忘了包括它。感谢您指出这一点,帖子已更新。
    • 谢谢你,这个问题困扰了我很长时间(我的意思是整个答案)但仍然没有办法直接编译和使用Expression&lt;Func&lt;...&gt;&gt;(x=&gt;x.IsDeleted),或者以某种方式接近跨度>
    【解决方案2】:

    我为我的答案找到了一个简单的解决方案 ;-)。无论如何,谢谢伊万·斯托耶夫

    界面是:

    public interface IDeletableEntity
    {
        bool IsDeleted { get; }
    }
    

    在您的模型生成器配置中:

    builder.Model.GetEntityTypes()
                           .Where(entityType => typeof(IDeletableEntity).IsAssignableFrom(entityType.ClrType))
                           .ToList()
                           .ForEach(entityType =>
                           {
                               builder.Entity(entityType.ClrType)
                               .HasQueryFilter(ConvertFilterExpression<IDeletableEntity>(e => !e.IsDeleted, entityType.ClrType));
                           });
    

    你需要转换filterExpression

    private static LambdaExpression ConvertFilterExpression<TInterface>(
                                Expression<Func<TInterface, bool>> filterExpression,
                                Type entityType)
                    {
                        var newParam = Expression.Parameter(entityType);
                        var newBody = ReplacingExpressionVisitor.Replace(filterExpression.Parameters.Single(), newParam, filterExpression.Body);
    
                        return Expression.Lambda(newBody, newParam);
                    }
    

    【讨论】:

      【解决方案3】:

      对@SamazoOo 的回答的一个小改进。您可以编写一个扩展方法,使其更加一致。

      public static EntityTypeBuilder HasQueryFilter<T>(this EntityTypeBuilder entityTypeBuilder, Expression<Func<T, bool>> filterExpression)
          {
              var param = Expression.Parameter(entityTypeBuilder.Metadata.ClrType);
              var body = ReplacingExpressionVisitor.Replace(filterExpression.Parameters.Single(), param, filterExpression.Body);
      
              var lambdaExp = Expression.Lambda(body, param);
      
              return entityTypeBuilder.HasQueryFilter(lambdaExp);
          }
      

      【讨论】:

        【解决方案4】:

        .net core 3.1 这对我不起作用,所以我尝试了以下有点相似的方法:

        // fetch entity types by reflection then: 
        
         softDeletedEntityTypes.ForEach(entityType =>
                    {
                        modelBuilder.Entity(entityType, builder =>
                        {
                            builder.Property<bool>("IsDeleted");
                            builder.HasQueryFilter(GenerateQueryFilterExpression(entityType));
                        });
                    });
        
        
         private static LambdaExpression GenerateQueryFilterExpression(Type entityType)
                {            
                     // the following lambda expression should be generated
                     // e => !EF.Property<bool>(e, "IsDeleted")); 
        
                    var parameter = Expression.Parameter(entityType, "e"); // e =>
        
                    var fieldName = Expression.Constant("IsDeleted", typeof(string)); // "IsDeleted"
        
                    // EF.Property<bool>(e, "IsDeleted")
                    var genericMethodCall = Expression.Call(typeof(EF), "Property", new[] {typeof(bool)}, parameter, fieldName);
        
                    // !EF.Property<bool>(e, "IsDeleted"))
                    var not = Expression.Not(genericMethodCall);
        
                    // e => !EF.Property<bool>(e, "IsDeleted"));
                    var lambda = Expression.Lambda(not, parameter);
                }
        
        

        【讨论】:

          【解决方案5】:

          我做的是

          builder.Model.GetEntityTypes()
                     .Where(p => typeof(IDeletableEntity).IsAssignableFrom(p.ClrType))
                     .ToList()
                      .ForEach(entityType =>
                      {
                          builder.Entity(entityType.ClrType)
                          .HasQueryFilter(ConvertFilterExpression<IDeletableEntity>(e => !e.IsDeleted, entityType.ClrType));
                      });
          

           private static LambdaExpression ConvertFilterExpression<TInterface>(
                          Expression<Func<TInterface, bool>> filterExpression,
                          Type entityType)
              {
                  var newParam = Expression.Parameter(entityType);
                  var newBody = ReplacingExpressionVisitor.Replace(filterExpression.Parameters.Single(), newParam, filterExpression.Body);
          
                  return Expression.Lambda(newBody, newParam);
              }
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2022-01-22
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2021-06-20
            相关资源
            最近更新 更多