假设我有这样一个函数:
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 知道在构建树并在树中使用其结果时需要执行 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[].Where(x => (x > 2))
这正是我们想要的。
请注意,在调用 EfUtil.ResultOf
的任何地方都不能使用 lambda 参数(这些示例中的 x
),否则代码将无法运行,并且不可能正常工作(尽管如果我们足够关心,我们可以生成更好的错误消息)。
关于c# - 有没有办法将外部函数内联到 EF Linq 查询中?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26744093/