Make GetCurrentFilter
(只读)属性而不是方法。与方法不同,EF 将计算属性的值,而不是尝试将它们转换为 SQL。
您拥有的唯一另一条路是遍历整个表达式树,搜索您的用法ResultOf
方法,将其参数计算为一个值,然后将该值内联到ResultOf
调用一次,围绕该值重建查询。
为了使其工作,这意味着您不仅需要将要内联的代码包装在调用中EfUtil.ResultOf
,但这也意味着在查询本身上调用一个方法来强制它返回并评估它:
public class EfUtil
{
public static T ResultOf<T>(T value)
{
return value;
}
}
//Note this could probably use a better name
public static IQueryable<T> EvaluateResults<T>(this IQueryable<T> query)
{
return query.Provider.CreateQuery<T>(
new ExpressionEvaluator().Visit(query.Expression));
}
internal class ExpressionEvaluator : ExpressionVisitor
{
protected override Expression VisitMethodCall(MethodCallExpression m)
{
if (m.Method.Name == "ResultOf" && m.Method.DeclaringType == typeof(EfUtil))
{
Expression target = m.Arguments[0];
object result = Expression.Lambda(target)
.Compile()
.DynamicInvoke();
return Expression.Constant(result, target.Type);
}
else
return base.VisitMethodCall(m);
}
}
这将允许你写:
var result = context.EntitySet.Where(x=> x.column > EfUtil.ResultOf(GetCurrentFilter(state)))
.EvaluateResults();
然后它会评估GetCurrentFilter(state)
在客户端并将结果作为常量内联到查询中。
作为一个稍微简单的测试,我们可以编写以下内容:
var query = new[] { 1, 2, 3 }
.AsQueryable()
.Where(x => x > EfUtil.ResultOf(Math.Max(1, 2)))
.EvaluateResults();
Console.WriteLine(query.ToString());
它会打印出:
System.Int32[].Where(x => (x > 2))
这正是我们想要的。
请注意 lambda 参数的使用 (x
在这些示例中)不能在调用中的任何地方使用EfUtil.ResultOf
否则代码将无法工作,并且不可能使其工作(尽管如果我们足够关心的话,我们可以生成更好的错误消息)。