我正在尝试动态构造一个类似于下面的表达式,其中我可以使用相同的比较函数,但是可以传入要比较的值,因为该值是从属性“更高”中传递的询问。
var people = People
.Where(p => p.Cars
.Any(c => c.Colour == p.FavouriteColour));
我相信我已经正确构建了查询,但是ExpressionExpander.VisitMethodCall(..)
当我尝试使用它时,方法抛出以下异常:
“无法将‘System.Linq.Expressions.InstanceMethodCallExpressionN’类型的对象转换为‘System.Linq.Expressions.LambdaExpression’类型”
在现实世界的代码中,使用实体框架和实际IQueryable<T>
,我经常得到:
“也无法将类型为“System.Linq.Expressions.MethodCallExpressionN”的对象转换为类型为“System.Linq.Expressions.LambdaExpression””。
我已经为我的问题构建了一个 LinqPad 友好的示例,尽可能简单。
void Main()
{
var tuples = new List<Tuple<String, int>>() {
new Tuple<String, int>("Hello", 4),
new Tuple<String, int>("World", 2),
new Tuple<String, int>("Cheese", 20)
};
var queryableTuples = tuples.AsQueryable();
// For this example, I want to check which of these strings are longer than their accompanying number.
// The expression I want to build needs to use one of the values of the item (the int) in order to construct the expression.
// Basically just want to construct this:
// .Where (x => x.Item1.Length > x.Item2)
var expressionToCheckTuple = BuildExpressionToCheckTuple();
var result = queryableTuples
.AsExpandable()
.Where (t => expressionToCheckTuple.Invoke(t))
.ToList();
}
public Expression<Func<string, bool>> BuildExpressionToCheckStringLength(int minLength) {
return str => str.Length > minLength;
}
public Expression<Func<Tuple<string, int>, bool>> BuildExpressionToCheckTuple() {
// I'm passed something (eg. Tuple) that contains:
// * a value that I need to construct the expression (eg. the 'min length')
// * the value that I will need to invoke the expression (eg. the string)
return tuple => BuildExpressionToCheckStringLength(tuple.Item2 /* the length */).Invoke(tuple.Item1 /* string */);
}
如果我做了一些明显错误的事情,我真的很感激能朝正确的方向推动!谢谢。
编辑:我知道以下方法可行:
Expression<Func<Tuple<string, int>, bool>> expr = x => x.Item1.Length > x.Item2;
var result = queryableTuples
.AsExpandable()
.Where (t => expr.Invoke(t))
.ToList();
但是,我试图将比较与参数的位置分开,因为比较可能很复杂,并且我想将其重新用于许多不同的查询(每个查询的两个参数都有不同的位置)。还希望其中一个参数(在示例中为“最小长度”)实际上可以通过另一个表达式来计算。
编辑:抱歉,我刚刚意识到,当尝试针对我的示例代码时,某些答案将会起作用,因为我的示例只是伪装成IQueryable<T>
但仍然是一个List<T>
下。我首先使用 LinqKit 的原因是因为一个实际的IQueryable<T>
来自 EntityFramework DbContext 将调用 Linq-to-SQL,因此必须能够由 Linq-to-SQL 本身进行解析。 LinqKit 通过将所有内容扩展为表达式来实现这一点。
解决方案!谢谢下面是Jean的回答 https://stackoverflow.com/a/23649736/590382,我想我已经意识到我错在哪里了。
如果值来自查询中的某个位置(即not事先已知的值。)那么您必须将其引用/表达式/变量构建到表达式中。
在我原来的示例中,我试图传递从表达式中获取的“minLength”值并将其传递给方法。该方法调用无法预先完成,因为它使用了表达式中的值,并且无法在表达式内完成,因为您无法在表达式内构建表达式。
那么,如何解决这个问题呢?我选择编写表达式,以便可以使用附加参数来调用它们。尽管这有一个缺点,即参数不再“命名”,我最终可能会得到一个Expression<Func<int, int, int, int, bool>>
或者接下来的事情。
// New signature.
public Expression<Func<string, int, bool>> BuildExpressionToCheckStringLength() {
// Now takes two parameters.
return (str, minLength) => str.Length > minLength;
}
public Expression<Func<Tuple<string, int>, bool>> BuildExpressionToCheckTuple() {
// Construct the expression before-hand.
var expression = BuildExpressionToCheckStringLength();
// Invoke the expression using both values.
return tuple => expression.Invoke(tuple.Item1 /* string */, tuple.Item2 /* the length */);
}