EF Core:使用阴影属性和查询filter进行软删除

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

public interface IDeletableEntity {} 

然后在我的模型构建器中

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

但是查询filter的行不能编译。 我得到的错误是“无法将lambda表达式转换为’lambda表达式’,因为它不是委托类型”

如果我这样做它的工作。

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

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

提前谢谢了,

非genericsEntityTypeBuilder HasQueryFilter (与通用的EntityTypeBuilder相反)几乎无法使用,因为没有简单的方法来创建预期的LambdaExpression

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

 .ForEach(entityType => { builder.Entity(entityType.ClrType).Property("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)); }); 

另一个是使用原型表达式并使用参数替换器将参数与实际类型绑定:

 .ForEach(entityType => { builder.Entity(entityType.ClrType).Property("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; } } 

但在我看来,最自然的解决方案是在通用方法中移动配置代码并通过reflection调用它。 例如:

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

然后

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

我为我的答案找到了一个简单的解决方案;-)。 非常感谢Ivan Stoev

界面是:

 public interface IDeletableEntity { bool IsDeleted { get; } } 

在您的模型Builder配置中:

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

你需要convertfilterExpression

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