如何将表达式 Expression<Func<T1, T2, bool>> 合并为单个 Expression<Func<T2, bool>>
How to combine expressions Expression<Func<T1, T2, bool>> to a single Expression<Func<T2, bool>>
我有一个由两个 Func 组成的条件列表:
public Func<TConfiguration, string> ConfigurationField { get;}
public Func<TNumbering, string> NumberingField { get; }
对于每个条件,表达式如下所示:
Expression<Func<TNumbering, TConfiguration, bool>> (n, c) => criteria.ConfigurationField(c) != criteria.NumberingField(n)
我需要用 OrElse 链接这些表达式的列表。
我试过做类似的事情:
BinaryExpression expression = null;
foreach (var criteria in SelectionCriteria)
{
Expression<Func<TNumbering, TConfiguration, bool>> exp = (n, c) => criteria.ConfigurationField(c) != criteria.NumberingField(n);
expression = expression == null ? exp : Expression.OrElse(expression, exp);
}
if (expression == null) return Result.Failure("Expression not defined"));
var lambda = Expression.Lambda<Func<TConfiguration, bool>>(expression);
numberingsToRemove = numberings.Where(_ => configurations.All(lambda));
但是,编译器不喜欢它,说 Expression.Lambda> 和二进制表达式之间没有隐式转换。
如果我用
expression = expression == null ? Expression.OrElse(exp, exp) : Expression.OrElse(expression, exp);
我明白了
The binary operator OrElse is not defined for the types 'System.Func<TNumbering,TConfiguration,System.Boolean> and 'System.Func<TNumbering,TConfiguration,System.Boolean>.
我是构建表达式的新手,有人可以为我指明正确的方向吗?
你的Expression<Func<TNumbering, TConfiguration, bool>>
是一个泛型类型,它的开放泛型类型是Expression<TDelegate>,其中TDelegate是一些委托类型;在这种情况下 Func<TNumbering, TConfiguration, bool>
.
Expression 继承自 LambdaExpression, which represents a C# (or VB.NET) lambda expression.
就像你不会写下面的代码:
var result =
(n, c) => criteria.ConfigurationField(c) != criteria.NumberingField(n) ||
(n1, c1) => criteria.ConfigurationField(c1) != criteria.NumberingField(n1);
尝试将两个 LambdaExpression 与 OrElse
结合起来会在运行时抛出异常。
您的代码甚至没有编译,因为 expression
被键入为 BinaryExpression,表示对应于此的表达式:
criteria.ConfigurationField(c) != criteria.NumberingField(n) ||
criteria.ConfigurationField(c1) != criteria.NumberingField(n1)
您试图将完整的 Expression 放入其中,其中包括(例如)参数列表。
每个LambdaExpression都有一个Body属性,从对应的表达式中提取:
(n, c) => criteria.ConfigurationField(c) != criteria.NumberingField(n)
LambdaExpression 的正文,或对应于此的表达式:
criteria.ConfigurationField(c) != criteria.NumberingField(n)
从理论上讲,您可以将其组合成对应于此的 BinaryExpression:
criteria.ConfigurationField(c) != criteria.NumberingField(n) ||
criteria.ConfigurationField(c1) != criteria.NumberingField(n1)
但这也行不通,因为每次迭代都会引入多个新参数,所有这些参数都必须传递给最终的 lambda。
可以解决这个问题,但我建议您首先将 SelectionCriteria
中的每个元素映射到对应于条件评估的表达式,使用 [ 中的工厂方法=75=]。然后,您可以将这些表达式组合成 BinaryExpression,然后可以将其包裹在 LambdaExpression 甚至 Expression.
它可能看起来像这样(做出一些假设):
class Criteria<TConfiguration, TNumbering> {
public Func<TConfiguration, string> ConfigurationField { get;}
public Func<TNumbering, string> NumberingField { get; }
}
// using static System.Linq.Expressions.Expression;
var SelectionCritera = new List<Criteria>();
/*
* populate list here
*/
var configParam = Parameter(typeof(TConfiguration));
var numberingParam = Parameter(typeof(TNumbering));
var expressions =
SelectionCriteria.Select(criteria => {
var criteriaExpr = Constant(criteria);
return NotEqual( // !=
Invoke( // ( ... )
PropertyOrField( // .ConfigurationField
criteriaExpr, // criteria
"ConfigurationField"
),
configParam // c
),
Invoke( // ( ... )
PropertyOrField( // .NumberingField
criteriaExpr, // criteria
"NumberingField"
),
numberingParam // n
)
);
})
.ToList();
if (!expressions.Any) { return Result.Failure("Expression not defined")); }
// Combine all the subexpressions using ||
var body = expressions.Aggregate((prev, next) => OrElse(prev, next));
// Create a LambdaExpression
var lmbd = Lambda<Func<TConfiguration, TNumbering, bool>>(body, configParam, numberingParam);
// Create a .NET method from the LambdaExpression
var mthd = lmbd.Compile();
// Apply the method to each config/numbering pair
var result = (
from config in configs
from numbering in numbering
select (config, numbering)
).All(x => mthd(config, numbering));
我有一个由两个 Func 组成的条件列表:
public Func<TConfiguration, string> ConfigurationField { get;}
public Func<TNumbering, string> NumberingField { get; }
对于每个条件,表达式如下所示:
Expression<Func<TNumbering, TConfiguration, bool>> (n, c) => criteria.ConfigurationField(c) != criteria.NumberingField(n)
我需要用 OrElse 链接这些表达式的列表。
我试过做类似的事情:
BinaryExpression expression = null;
foreach (var criteria in SelectionCriteria)
{
Expression<Func<TNumbering, TConfiguration, bool>> exp = (n, c) => criteria.ConfigurationField(c) != criteria.NumberingField(n);
expression = expression == null ? exp : Expression.OrElse(expression, exp);
}
if (expression == null) return Result.Failure("Expression not defined"));
var lambda = Expression.Lambda<Func<TConfiguration, bool>>(expression);
numberingsToRemove = numberings.Where(_ => configurations.All(lambda));
但是,编译器不喜欢它,说 Expression.Lambda
如果我用
expression = expression == null ? Expression.OrElse(exp, exp) : Expression.OrElse(expression, exp);
我明白了
The binary operator OrElse is not defined for the types 'System.Func<TNumbering,TConfiguration,System.Boolean> and 'System.Func<TNumbering,TConfiguration,System.Boolean>.
我是构建表达式的新手,有人可以为我指明正确的方向吗?
你的Expression<Func<TNumbering, TConfiguration, bool>>
是一个泛型类型,它的开放泛型类型是Expression<TDelegate>,其中TDelegate是一些委托类型;在这种情况下 Func<TNumbering, TConfiguration, bool>
.
Expression
就像你不会写下面的代码:
var result =
(n, c) => criteria.ConfigurationField(c) != criteria.NumberingField(n) ||
(n1, c1) => criteria.ConfigurationField(c1) != criteria.NumberingField(n1);
尝试将两个 LambdaExpression 与 OrElse
结合起来会在运行时抛出异常。
您的代码甚至没有编译,因为 expression
被键入为 BinaryExpression,表示对应于此的表达式:
criteria.ConfigurationField(c) != criteria.NumberingField(n) ||
criteria.ConfigurationField(c1) != criteria.NumberingField(n1)
您试图将完整的 Expression
每个LambdaExpression都有一个Body属性,从对应的表达式中提取:
(n, c) => criteria.ConfigurationField(c) != criteria.NumberingField(n)
LambdaExpression 的正文,或对应于此的表达式:
criteria.ConfigurationField(c) != criteria.NumberingField(n)
从理论上讲,您可以将其组合成对应于此的 BinaryExpression:
criteria.ConfigurationField(c) != criteria.NumberingField(n) ||
criteria.ConfigurationField(c1) != criteria.NumberingField(n1)
但这也行不通,因为每次迭代都会引入多个新参数,所有这些参数都必须传递给最终的 lambda。
可以解决这个问题,但我建议您首先将 SelectionCriteria
中的每个元素映射到对应于条件评估的表达式,使用 [ 中的工厂方法=75=]。然后,您可以将这些表达式组合成 BinaryExpression,然后可以将其包裹在 LambdaExpression 甚至 Expression.
它可能看起来像这样(做出一些假设):
class Criteria<TConfiguration, TNumbering> {
public Func<TConfiguration, string> ConfigurationField { get;}
public Func<TNumbering, string> NumberingField { get; }
}
// using static System.Linq.Expressions.Expression;
var SelectionCritera = new List<Criteria>();
/*
* populate list here
*/
var configParam = Parameter(typeof(TConfiguration));
var numberingParam = Parameter(typeof(TNumbering));
var expressions =
SelectionCriteria.Select(criteria => {
var criteriaExpr = Constant(criteria);
return NotEqual( // !=
Invoke( // ( ... )
PropertyOrField( // .ConfigurationField
criteriaExpr, // criteria
"ConfigurationField"
),
configParam // c
),
Invoke( // ( ... )
PropertyOrField( // .NumberingField
criteriaExpr, // criteria
"NumberingField"
),
numberingParam // n
)
);
})
.ToList();
if (!expressions.Any) { return Result.Failure("Expression not defined")); }
// Combine all the subexpressions using ||
var body = expressions.Aggregate((prev, next) => OrElse(prev, next));
// Create a LambdaExpression
var lmbd = Lambda<Func<TConfiguration, TNumbering, bool>>(body, configParam, numberingParam);
// Create a .NET method from the LambdaExpression
var mthd = lmbd.Compile();
// Apply the method to each config/numbering pair
var result = (
from config in configs
from numbering in numbering
select (config, numbering)
).All(x => mthd(config, numbering));