我有一个场景,我需要使用linq(使用nhibernate)来查看动态查询。最终查询应如下所示:
long[] values = { ... };
var result = Queryable<Entity>.Where(x => x.Documents.Any(d => values.Contains(d.Id)))
.ToList();
通用Entity
和属性Documents
可以更改,它将由某些用户配置定义。集合Documents
的类型为ICollection<T>
,其中T
为Document
类型。我正在尝试创建一个Expression
树来动态定义这些语句,但我遇到了一些问题。看看我试过的代码和评论。
我创建了这个函数来返回我想在Any
方法中使用的delagate:
public static Func<T, bool> GetFunc<T>(long[] values)
where T : Entity
{
return x => values.Contains(x.Id);
}
我正在使用Expression类来创建这样的表达式(请参阅代码和注释):
// define my parameter of expression
var parameter = Expression.Parameter(typeof(T), "x");
// I get an array of IDs (long) as argument and transform it on an Expression
var valuesExpression = Expression.Constant(values);
// define the access to my collection property. propertyFilter is propertyinfo for the `Documents` of the sample above.
// I get an expression to represent: x.Documents
var collectionPropertyExpression = Expression.Property(parameter, propertyFilter);
// get the T generic type of the ICollection<T> from propertyFilter. I get the `Documents` of sample above.
var entityFilterType = propertyFilter.PropertyType.GetGenericArguments()[0];
// get the definition of `Any` extension method from `Enumerable` class to make the expression
var anyMethod = typeof(Enumerable).GetMethods(BindingFlags.Static | BindingFlags.Public)
.First(x => x.Name == "Any" && x.GetParameters().Length == 2)
.MakeGenericMethod(entityFilterType);
// get a methodBase for GetFunc to get the delagete to use inside the Any
// using the `Document` generic type
var collectionBody = typeof(LookUpHelper).GetMethod("GetFunc", BindingFlags.Public | BindingFlags.Static)
.MakeGenericMethod(entityFilterType);
// call the any passing the collection I need and convert it to a Delegate
// I get something like: x => values.Contains(x.Id) ... where x if the `Document`
var func = (Delegate)collectionBody.Invoke(null, new object[] { values });
// get the func as an expression .. maybe the problem is here
var funcExpression = Expression.Constant(func);
// call the any passing the collection and my delagate as arguments
var f = Expression.Call(anyMethod, collectionPropertyExpression, funcExpression);
// I already have an expression and concatenate it using `AndAlso` operator.
body = Expression.AndAlso(body, f);
// finally, I built up to lambda expression and apply it on my queryable
var filterExpression = Expression.Lambda<Func<T, bool>>(body, parameter);
var result = Queryable.Where(filterExpression).ToList();
执行直到ToList
方法执行查询。我收到以下错误:
无法解析表达式 'x.Documents.Any(值(System.Func`2 [Project.Document,System.Boolean]))': “System.Linq.Expressions.ConstantExpression”类型的对象不能 转换为'System.Linq.Expressions.LambdaExpression'类型。如果 你尝试传递委托而不是LambdaExpression,这是 不受支持,因为委托不是可解析的表达式。
我不确定我做错了什么。有人可以帮帮我吗?
谢谢。
答案 0 :(得分:1)
您传递Func
,预计会有Expression<Func>
。前者是代表,后者是表达。
public static Expression<Func<T, bool>> GetFunc<T>(long[] values)
where T : Entity
{
return x => values.Contains(x.Id);
}
现在您放弃了需要使用表达式助手类手动构建表达式,因为您已经有了表达式。