你正在混合表达式 with lambda 表达式。有很多帖子展示了如何组合 lambda 表达式,但最重要的部分是从 lambda 表达式组合表达式bodies并重新绑定参数.
后者通常是通过自定义来实现的ExpressionVisitor像这样:
using System.Linq.Expressions;
public static class ExpressionExtensions
{
public static Expression ReplaceParameter(this Expression expression, ParameterExpression source, Expression target)
{
return new ParameterReplacer { Source = source, Target = target }.Visit(expression);
}
class ParameterReplacer : ExpressionVisitor
{
public ParameterExpression Source;
public Expression Target;
protected override Expression VisitParameter(ParameterExpression node)
{
return node == Source ? Target : base.VisitParameter(node);
}
}
}
现在介绍 EF Core 组合查询过滤器。
对于您正在做的事情来说,使用字典和表达式列表似乎过于复杂。自从IMutableEntityType提供读/写访问QueryFilter,同样可以通过一小组自定义扩展方法来实现。
他们都进入这样的班级:
using System;
using System.Linq.Expressions;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
public static class QueryFilterExtensions
{
}
第一种方法:
public static void AddQueryFilter(this IMutableEntityType target, LambdaExpression filter)
{
if (target.QueryFilter == null)
target.QueryFilter = filter;
else
{
var parameter = target.QueryFilter.Parameters[0];
var left = target.QueryFilter.Body;
var right = filter.Body.ReplaceParameter(filter.Parameters[0], parameter);
var body = Expression.AndAlso(left, right);
target.QueryFilter = Expression.Lambda(body, parameter);
}
}
这是一种非通用方法,它将现有过滤器与通过的过滤器结合使用AndAlso
(C# &&
) 运算符并展示了前面提到的 lambda 表达式组合原理。
然而,它并不是那么直接有用,就像在实体类型配置循环中一样(它可以,但需要您手动构建 lambda 表达式,而不是让 C# 编译器这样做)。那么这里就出现了第二种方法:
public static void AddQueryFilter<T>(this IMutableEntityType target, Expression<Func<T, bool>> filter)
{
LambdaExpression targetFilter = filter;
if (target.ClrType != typeof(T))
{
var parameter = Expression.Parameter(target.ClrType, "e");
var body = filter.Body.ReplaceParameter(filter.Parameters[0], parameter);
targetFilter = Expression.Lambda(body, parameter);
}
target.AddQueryFilter(targetFilter);
}
它是一个通用方法 - 不太类型安全,但允许您使用编译时 lambda 表达式并将其绑定到实际实体类型,如下所示:
foreach (var entityType in modelBuilder.Model.GetEntityTypes())
{
if (typeof(IDeleted).IsAssignableFrom(entityType.ClrType))
entityType.AddQueryFilter<IDeleted>(e => e.DateTimeDeleted == null);
}
看起来好多了,不是吗:)
最后一个自定义扩展方法是对标准 EF Core 泛型的补充(替换)HasQueryFilter method:
public static EntityTypeBuilder<TEntity> AddQueryFilter<TEntity>(this EntityTypeBuilder<TEntity> target, Expression<Func<TEntity, bool>> filter)
where TEntity : class
{
target.Metadata.AddQueryFilter(filter);
return target;
}
并允许您替换
this.AddToDict<DbMyEntity>(t => t.Name == null || t.Name == "Something");
与更方便的
modelBuilder.Entity<DbMyEntity>()
.AddQueryFilter(t => t.Name == null || t.Name == "Something");
更新(EF Core 3.0): QueryFilter
属性已替换为GetQueryFilter
and SetQueryFilter
扩展方法。