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