如何合并Categoria
和BinaryExpression
?
例如:
Expression<Func<dynamic / T, bool>>
我正在尝试为实体框架(EF)核心定义global filter。问题是我必须manually combine multiple filters。
如果模型实现了void AddGlobalFilter<T>(Expression<Func<T, bool>> expr)
{
var parameter = Expression.Parameter(type, "t");
var member = Expression.Property(filter.Parameter, field);
var constant = Expression.Constant(null);
var body = Expression.Equal(member, constant);
var combine = Expression.AndAlso(body, expr);
}
接口,则可以在ModelBuilder
中添加一个过滤器。
可以为特定模型手动添加另一个。基本思想是我拥有所有表达式的列表,然后将它们组合:
IDbDeleted
我当然会出错(第一个来自var expression = listExpressions.First();
foreach (var second in listExpressions.Skip(1))
{
expression = Expression.AndAlso(expression, second);
}
var lambdaExpression = Expression.Lambda(expression, parameter);
modelBuilder.Entity(item.Key).HasQueryFilter(lambdaExpression);
,第二个来自Expression.Equal
):
过滤器表达式't => t =>(Not(t。...
已编辑:代码如下所示:
t => t...
答案 0 :(得分:4)
您正在将expressions与lambda expressions混合在一起。有很多文章显示了如何组合lambda表达式,但是必不可少的部分是从lambda表达式bodies组成表达式并重新绑定parameters。
后者通常是通过这样的自定义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
方法的补充(替代):
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");