我使用此代码,但出现错误:
错误:System.ArgumentException:“...GreenPaperItem”类型的表达式不能用于方法“Boolean”的“System.Runtime.CompilerServices.Closure”类型的参数
public static Expression<Func<T, bool>> ToExpression<T>(Func<T, bool> p)
{
ParameterExpression p0 = Expression.Parameter(typeof(T));
ParameterExpression p1 = Expression.Parameter(typeof(bool));
return Expression.Lambda<Func<T, bool>>(Expression.Call(p.Method, p0, p1),
new ParameterExpression[] { p0, p1 });
}
该表达式旨在用于实体 IQueriyable 查询的 linq:
query = query.Where(ToExpression<GreenPaperItem>(filter.GenerateFilterFunction()));
如何将
转换为Func<T, bool>
Expression<Func<T, bool>>
Expression<TDelegate>.Compile
方法提供的转换是“容易”的。但其他方法将需要执行诸如从 IL/机器代码反编译之类的操作(我想我看到了一些尝试)。
我使用了 AsQueryable() 并且它有效,但是为了检查它最后是否不可枚举,我收到错误“给定的‘IQueryable’不支持生成查询字符串。”当我想将查询转换为ToQueryString()
所以基本上它不起作用(至少以你想要/需要的方式),它只是编译。
您似乎对
IQueryable
、表达式树和查询提供程序(尤其是 EF Core 之一)的工作方式存在一些误解。简而言之(经过一些简化)通常查询提供程序将分析传递的表达式树以执行某些操作。在 EF Core 情况下,它会将调用转换为实际的 SQL 查询(对于数据库),并且无法将任意方法调用转换为 SQL,这基本上就是您试图通过“转换”由 Func<...>
返回的
filter.GenerateFilterFunction()
来实现的操作到
Expression<Func<...>>
。您的尝试与以下内容没有太大不同:
query.Where(gpi => filter.GenerateFilterFunction()(gpi)) // allow the compiler to do the magic for you
这将有同样的问题 -
GenerateFilterFunction
返回函数查询提供程序(EF Core)一无所知,并且无法将其转换为 SQL(出于明显的原因)。
所以你的尝试:
query = query
.Where(filter.GenerateFilterFunction())
.AsQueryable();
将导致自
.Where(filter.GenerateFilterFunction())
以来的所有内容都将在客户端执行,将所有内容提取到内存中,而不在数据库端进行过滤(将使用Enumerable.Where(Func<>)
而不是Queryable.Where(Expression<Func<>>)
,与显式效果基本相同) AsEnumerable()
先打电话Where
)。以下 AsQueryable
没有任何显着效果,因为它将可通过内存中的对象进行查询。
该做什么:
您有多种选择 - 引入
GenerateFilterExpression
方法,该方法将实际构建可翻译的 Expression<Func<T, bool>>
非常常见的方法是利用“动态”撰写
Where
的能力:
if(filter.IsProp1Provided)
{
query = query.Where(e => e.Col1 == filter.Prop1);
}
if(filter.IsAnotherPropProvided)
{
query = query.Where(e => e.AnotherCol == filter.AnotherProp);
}
使用 LINQKit 中的
PredicateBuilder
(可能除了第 2 点之外)
另请参阅: