具有嵌套属性的动态 linq 表达式树

Posted

技术标签:

【中文标题】具有嵌套属性的动态 linq 表达式树【英文标题】:Dynamic linq expression tree with nested properties 【发布时间】:2017-03-14 18:50:06 【问题描述】:

我有一个必须过滤子属性的列表。过滤器运算符是动态的,我使用谓词构建器来组合多个过滤器/lambda。

为简单起见,假设我有两个这样的类:

public class FirstClass

    public int Id  get; set; 
    public ICollection<SecondClass> MyList  get; set; 


public class SecondClass

    public int ReferenceId  get; set; 
    public int Value  get; set; 

我的过滤器使用一个引用 id、一个操作符类型和一个值,这样一个伪代码会是这样的:

"list of FirstClass".Where(param1 => 
    param1.MyList.Single(param2 => 
        param2.ReferenceId == "reference id").Value "operatorType" "value")

实际的过滤器类似于123 eq 456,其中引用id 是123,operatorType 是“eq”,值是456。

如果运算符只是相等,那么以下工作就可以了:

Expression<Func<FirstClass, bool>> lambda = 
    param1 => param1.MyList.Single(param2 => param2.ReferenceId == id).Value == value;

此外,仅使用动态表达式过滤 FirstClass,就像一个魅力,例如过滤 Id(我的 ExpressionTypeDictionary 是一个字典,用于根据提供的 operatorType 选择 ExpressionType):

var parameter = Expression.Parameter(typeof(FirstClass), "param1");
Expression body = parameter;
body = Expression.Property(body, "Id");
body = Expression.MakeBinary(ExpressionTypeDictionary[operatorType], body, value);
var lambda = Expression.Lambda<Func<FirstClass, bool>>(body, new[]  parameter );

我能够编译以下内容,但使用 EF Core 对真实数据执行过滤器会返回 querySource 异常:

var parameter = Expression.Parameter(typeof(FirstClass), "param1");
Expression<Func<FirstClass, int>> left = param1 => 
    param1.MyClass.Single(param2 => param2.ReferenceId == id).Value;
var body = Expression.MakeBinary(
    ExpressionTypeDictionary[operatorType], 
    left.Body, 
    Expression.Constant(value));
var lambda = Expression.Lambda<Func<FirstClass, bool>>(body, new[]  parameter );
...
theList.Where(lambda);

任何建议表示赞赏:)

【问题讨论】:

您可以使用 Gridify 库。 github.com/alirezanet/gridify 【参考方案1】:

我认为而不是这样的表达

Expression<Func<FirstClass, bool>> predicate = 
    x => x.MyList.Single(y => y.ReferenceId == id).Value [operator] value;

你最好构建一个这样的表达式:

Expression<Func<FirstClass, bool>> predicate = 
    x => x.MyList.Any(y => y.ReferenceId == id && y.Value == value);

您可以这样做:

var innerParameter = Expression.Parameter(typeof(SecondClass), "y");
var innerPredicate = Expression.Lambda<Func<SecondClass, bool>>(
    Expression.AndAlso(
        Expression.Equal(Expression.Property(innerParameter, "ReferenceId"), Expression.Constant(id)),
        Expression.MakeBinary(ExpressionTypeDictionary[operatorType], Expression.Property(innerParameter, "Value"), Expression.Constant(value))),
    innerParameter);
var parameter = Expression.Parameter(typeof(FirstClass), "x");
var predicate = Expression.Lambda<Func<FirstClass, bool>>(
    Expression.Call(
        typeof(Enumerable), "Any", new Type[]  typeof(SecondClass) ,
        Expression.Property(parameter, "MyList"), innerPredicate),
    parameter);

【讨论】:

您的建议让我更进一步,但我猜 EF Core 中存在一个错误(我使用的是 1.0.0 版)导致Expression.And 在 SQL 查询中转换为&amp; .通过改用Expression.AndAlso,SQL 语法很好,一切都按预期工作。 哎呀,对不起,应该是AndAlso,我总是把他们搞砸了:)

以上是关于具有嵌套属性的动态 linq 表达式树的主要内容,如果未能解决你的问题,请参考以下文章

LINQ to Entities OrderBy 表达式树

如何构建具有多个条件的 Lambda 表达式树

什么都2020了,LINQ查询你还在用表达式树

表达式树与委托

LINQ 表达式树是正确的树吗?

如何创建 LINQ 表达式树以选择匿名类型