C# 试图将父属性用作子集合表达式中的参数;林奇抛出;无法将MethodCallExpressionN强制转换为LambdaExpression“;
我试图动态构造一个类似于下面的表达式,在这里我可以使用相同的比较函数,但是可以传入被比较的值,因为该值是从查询中的“更高”属性传递的C# 试图将父属性用作子集合表达式中的参数;林奇抛出;无法将MethodCallExpressionN强制转换为LambdaExpression“;,c#,linq,expression,linq-expressions,linqkit,C#,Linq,Expression,Linq Expressions,Linqkit,我试图动态构造一个类似于下面的表达式,在这里我可以使用相同的比较函数,但是可以传入被比较的值,因为该值是从查询中的“更高”属性传递的 var people=people .其中(p=>p.汽车 。任何(c=>c.颜色==p.FavoriteColour)); 我相信我已经正确地构造了查询,但是ExpressionExpander.VisitMethodCall(..)方法在我尝试使用它时抛出以下异常: “无法将'System.Linq.Expressions.InstanceMethodCal
var people=people
.其中(p=>p.汽车
。任何(c=>c.颜色==p.FavoriteColour));
我相信我已经正确地构造了查询,但是ExpressionExpander.VisitMethodCall(..)
方法在我尝试使用它时抛出以下异常:
“无法将'System.Linq.Expressions.InstanceMethodCallExpressionN'类型的对象强制转换为'System.Linq.Expressions.LambdaExpression'类型”
在实际代码中,使用实体框架和实际的IQueryable
,我经常得到:
“无法将'System.Linq.Expressions.MethodCallExpressionN'类型的对象强制转换为'System.Linq.Expressions.LambdaExpression'”类型。”
我已经为我的问题构建了一个LinqPad友好的示例,尽可能简单
void Main()
{
var tuples=新列表(){
新元组(“Hello”,4),
新元组(“世界”,2),
新元组(“奶酪”,20)
};
var queryableTuples=tuples.AsQueryable();
//对于本例,我想检查这些字符串中的哪些字符串比其附带的数字长。
//我要构建的表达式需要使用项的一个值(int)来构建表达式。
//基本上,我只想构建这个:
//.其中(x=>x.Item1.Length>x.Item2)
var expressionToCheckTuple=BuildExpressionToCheckTuple();
var result=queryableTuples
.AsExpandable()
.Where(t=>expressionToCheckTuple.Invoke(t))
.ToList();
}
公共表达式BuildExpressionToCheckStringLength(int minLength){
返回str=>str.Length>minLength;
}
公共表达式BuildExpressionToCheckTuple(){
//我收到了包含以下内容的内容(如元组):
//*构造表达式所需的值(例如“最小长度”)
//*调用表达式所需的值(例如字符串)
return tuple=>BuildExpressionToCheckStringLength(tuple.Item2/*长度*/).Invoke(tuple.Item1/*字符串*/);
}
如果我做了一些明显错误的事情,我真的很感激你朝着正确的方向轻推我!谢谢
编辑:我知道以下方法可行:
Expression<Func<Tuple<string, int>, bool>> expr = x => x.Item1.Length > x.Item2;
var result = queryableTuples
.AsExpandable()
.Where (t => expr.Invoke(t))
.ToList();
表达式expr=x=>x.Item1.Length>x.Item2;
var result=queryableTuples
.AsExpandable()
.Where(t=>expr.Invoke(t))
.ToList();
但是,我试图将比较与参数的位置分开,因为比较可能很复杂,我希望将其重新用于许多不同的查询(两个参数的位置各不相同)。还打算通过另一个表达式实际计算其中一个参数(在示例中为“最小长度”)
编辑:很抱歉,我刚刚意识到,当尝试针对我的示例代码时,一些答案会起作用,因为我的示例只是伪装成一个
IQueryable
,但下面仍然是一个列表。我之所以使用LinqKit,首先是因为EntityFramework DbContext中的实际IQueryable
将调用Linq to SQL,因此必须能够被Linq解析为SQL本身。LinqKit通过将所有内容扩展到表达式来实现这一点
解决方案多亏了,我想我已经意识到我错在哪里了
如果某个值来自查询中的某个位置(即不是一个已知的值),则必须将其引用/表达式/变量构建到表达式中
在我最初的示例中,我试图传递从表达式中获取的“minLength”值,并将其传递给一个方法。该方法调用不能提前完成,因为它使用了表达式中的值,也不能在表达式中完成,因为无法在表达式中构建表达式
那么,如何避免这种情况呢?我选择编写表达式,以便可以使用其他参数调用它们。虽然这样做的缺点是参数不再“命名”,我可能会得到一个表达式或其他类似的东西
//新签名。
公共表达式BuildExpressionToCheckStringLength(){
//现在需要两个参数。
返回(str,minLength)=>str.Length>minLength;
}
公共表达式BuildExpressionToCheckTuple(){
//在手之前构造表达式。
var expression=BuildExpressionToCheckStringLength();
//使用这两个值调用表达式。
return tuple=>expression.Invoke(tuple.Item1/*string*/,tuple.Item2/*长度*/);
}
因此,您正在寻找类似以下内容:
public static class Program
{
public class Person
{
public string FirstName { get; set; }
public string LastName { get; set; }
}
public static IQueryable<T> WherePropertyEquals<T, TProperty>(
this IQueryable<T> src, Expression<Func<T, TProperty>> property, TProperty value)
{
var result = src.Where(e => property.Invoke(e).Equals(value));
return result;
}
public static IQueryable<T> WhereGreater<T, TProperty>(
this IQueryable<T> src, Expression<Func<T, TProperty>> property, TProperty value)
where TProperty : IComparable<TProperty>
{
var result = src.Where(e => property.Invoke(e).CompareTo(value) > 0);
return result;
}
public static IQueryable<T> WhereGreater<T, TProperty>(
this IQueryable<T> src, Expression<Func<T, TProperty>> left, Expression<Func<T, TProperty>> right)
where TProperty : IComparable<TProperty>
{
var result = src.Where(e => left.Invoke(e).CompareTo(right.Invoke(e)) > 0);
return result;
}
public static void Main()
{
var persons = new List<Person>()
{
new Person
{
FirstName = "Jhon",
LastName = "Smith"
},
new Person
{
FirstName = "Chuck",
LastName = "Norris"
},
new Person
{
FirstName = "Ben",
LastName = "Jenkinson"
},
new Person
{
FirstName = "Barack",
LastName = "Obama"
}
}
.AsQueryable()
.AsExpandable();
var chuck = persons.WherePropertyEquals(p => p.FirstName, "Chuck").First();
var ben = persons.WhereGreater(p => p.LastName.Length, 6).First();
var barack = persons.WhereGreater(p => p.FirstName.Length, p => p.LastName.Length).First();
}
公共静态类程序
{
公共阶层人士
{
公共字符串名{get;set;}
公共字符串LastName{get;set;}
}
公共静态可查询属性相等的位置(
此IQueryable src、表达式属性、TProperty值)
{
var result=src.Where(e=>property.Invoke(e).Equals(value));
返回结果;
}
公共静态IQueryable(可在更大的地方使用)(
此IQueryable src、表达式属性、TProperty值)
其中t属性:i可比较
{
var result=src.Where(e=>property.Invoke(e).CompareTo(value)>0);
返回结果;
}
公共静态IQueryable(可在更大的地方使用)(
此IQueryable src,表达式左,表达式右)
其中t属性:i可比较
{
var result=src.Where(e=>left.Invoke(e).CompareTo(right.Invo
public Expression<Func<int, Func<string, bool>>> ExpressionToCheckStringLengthBuilder() {
return minLength =>
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)
// Putting builder into a variable so that the resulting expression will be
// visible to tools that analyze the expression.
var builder = ExpressionToCheckStringLengthBuilder();
return tuple => builder.Invoke(tuple.Item2 /* the length */).Invoke(tuple.Item1 /* string */);
}