在C#中结合BinaryExpression和Expression <func <dynamic,bool =“” >>

时间:2018-07-24 11:08:28

标签: c# lambda entity-framework-core expression-trees entity-framework-core-2.1

如何合并CategoriaBinaryExpression

例如:

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...

1 个答案:

答案 0 :(得分:4)

您正在将expressionslambda 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");