我正在尝试以编程方式构建表达式树。
我在输入中有条件表的列表,其格式如下:
public class Filter
{
public string field { get; set; }
public string operator { get; set; }
public string value { get; set; }
}
[当我构建Expression
对象时,我会以以下方式为每个条件创建一个Expression
foreach ( Filter sf in rules ) {
Expression ex = sf.ToExpression( query );
if ( mainExpression == null ) {
mainExpression = ex;
}
else {
if ( logicalCondition == "AND" ) {
mainExpression = Expression.And( mainExpression, ex );
}
else if ( logicalCondition == "OR" ) {
mainExpression = Expression.Or( mainExpression, ex );
}
}
}
[Filter.ToExpression()方法是这样实现的
public override Expression ToExpression( IQueryable query ) {
ParameterExpression parameter = Expression.Parameter( query.ElementType, "p" );
MemberExpression memberAccess = null;
foreach ( var property in field.Split( '.' ) )
memberAccess = MemberExpression.Property( memberAccess ?? ( parameter as Expression ), property );
ConstantExpression filter = Expression.Constant( Convert.ChangeType( value, memberAccess.Type ) );
WhereOperation condition = (WhereOperation)StringEnum.Parse( typeof( WhereOperation ), operator );
LambdaExpression lambda = BuildLambdaExpression( memberAccess, filter, parameter, condition, value );
return lambda;
}
[当我有一个条件时一切正常,但是当我尝试使用And
,Or
,AndAlso
,OrElse
静态方法之一组合表达式时,我收到的InvalidOperationException
表示:
没有为类型定义二进制运算符Or'System.Func
2[MyObject,System.Boolean]' and 'System.Func
2 [MyObject,System.Boolean]'。
我有点困惑。有人可以更好地解释异常的原因并提出解决方案吗?
非常感谢!
您正在将a => a == 3
和a => a == 4
合并为(a => a == 3) || (a => a == 4)
,但您应该尝试将其设为a => (a == 3 || a == 4)
。手动操作并不难,但可以someone has done it for you already。查找“合并表达式”。
Edit:根据要求,提供一个有关如何手动执行此操作的简单示例。
Edit 2:它使用.NET 4的ExpressionVisitor
,但at MSDN you can find a usable implementation for earlier versions。我假设您没有将MSDN代码视为“第三方”。您只需要将protected virtual Expression Visit(Expression exp)
方法更改为public
。而且由于Enumerable.Zip
对您不可用,并且没有必要,因此它现在不存在了。
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Linq.Expressions;
namespace DemoApp
{
<include ExpressionVisitor definition here for .NET 3.5>
public class ExpressionParameterReplacer : ExpressionVisitor
{
public ExpressionParameterReplacer(IList<ParameterExpression> fromParameters, IList<ParameterExpression> toParameters)
{
ParameterReplacements = new Dictionary<ParameterExpression, ParameterExpression>();
for (int i = 0; i != fromParameters.Count && i != toParameters.Count; i++)
ParameterReplacements.Add(fromParameters[i], toParameters[i]);
}
private IDictionary<ParameterExpression, ParameterExpression> ParameterReplacements
{
get;
set;
}
protected override Expression VisitParameter(ParameterExpression node)
{
ParameterExpression replacement;
if (ParameterReplacements.TryGetValue(node, out replacement))
node = replacement;
return base.VisitParameter(node);
}
}
class Program
{
static void Main(string[] args)
{
Expression<Func<int, bool>> exprA = a => a == 3;
Expression<Func<int, bool>> exprB = b => b == 4;
Expression<Func<int, bool>> exprC =
Expression.Lambda<Func<int, bool>>(
Expression.OrElse(
exprA.Body,
new ExpressionParameterReplacer(exprB.Parameters, exprA.Parameters).Visit(exprB.Body)),
exprA.Parameters);
Console.WriteLine(exprA.ToString());
Console.WriteLine(exprB.ToString());
Console.WriteLine(exprC.ToString());
Func<int, bool> funcA = exprA.Compile();
Func<int, bool> funcB = exprB.Compile();
Func<int, bool> funcC = exprC.Compile();
Debug.Assert(funcA(3) && !funcA(4) && !funcA(5));
Debug.Assert(!funcB(3) && funcB(4) && !funcB(5));
Debug.Assert(funcC(3) && funcC(4) && !funcC(5));
}
}
}