c# - 有没有办法将外部函数内联到 EF Linq 查询中?

标签 c# .net linq entity-framework

假设我有这样一个函数:

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/

相关文章:

c# - 如何获取使用 facebook 注册应用程序注册我的网站的 facebook 用户的个人资料照片(不使用 mvc)

c# - VS 2015 中的连接字符串

c# - 通过动态引用访问嵌套类的成员时发生 StackOverflowException

c# - 更新数据库集合的最佳策略

c# - 如何创建没有选项卡标题的 TabControl?

javascript - 如何根据键盘上的位置转换字符?

c# - AssemblyResolve 事件在调用 Assembly.Load(byte()) 时触发

c# - 如何将 String[] 转换为 IDictionary<String, String>?

c# - LINQ,Where() 与 FindAll()

c# - 动态 linq 多个与单个 .where 查询