有没有一种方法可以将外部函数内联到EF-Linq查询中
本文关键字:函数 外部 查询 EF-Linq 一种 方法 有没有 | 更新日期: 2023-09-27 18:29:01
假设我有一个这样的函数:
var filterValue = GetCurrentFilter(state);
然后是EF查询:
var result = context.EntitySet.Where(x=> x.column > filterValue);
这是有效的,但只要我尝试内联:
var result = context.EntitySet.Where(x=> x.column > GetCurrentFilter(state));
这并不是因为EF Linq试图将GetCurrentFilter
解析到表达式树中,但无法做到这一点。这一切都是可以理解的。
我的问题是,有没有办法让EF Linq知道in在构建树并在树中使用其结果时需要执行GetCurrentFilter
函数?
类似的东西
var result = context.EntitySet.Where(x=> x.column > EfUtil.ResultOf(GetCurrentFilter(state)));
由于GetCurrentFilter没有作为查询一部分的参数,如果EF-Linq能够支持它,那么从技术上讲,这应该是可能的。我怀疑我只是缺少正确的语法。
将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[]。其中(x=>(x>2))
这正是我们想要的。
请注意,lambda参数(在这些示例中为x
)的使用不能在对EfUtil.ResultOf
的调用中的任何位置使用,否则代码将无法工作,也不可能工作(尽管如果我们足够关心的话,我们可以生成更好的错误消息)。