有没有办法将外部函数内联到EF Linq查询中? [英] Is there a way to inline external functions into an EF Linq query?

查看:57
本文介绍了有没有办法将外部函数内联到EF Linq查询中?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

让我说一个像这样的函数:

Let say I have a function like this:

var filterValue = GetCurrentFilter(state);

然后进行EF查询:

var result = context.EntitySet.Where(x=> x.column > filterValue);

这可行,但是一旦我尝试内联:

this works, but as soon as I try to inline that:

var result = context.EntitySet.Where(x=> x.column > GetCurrentFilter(state));

这不是因为EF Linq试图将GetCurrentFilter解析为表达式树而无法执行.这一切都是可以理解的.

It does not because EF Linq tried to parse GetCurrentFilter into expression tree and is unable to do that. This is all quite understandable.

我的问题是,有没有办法让EF Linq知道在构建树并在树中使用其结果时需要执行GetCurrentFilter函数?

My question is, is there a way to let EF Linq know that in needs to execute the GetCurrentFilter function when it builds the tree and use its result in the tree?

类似

var result = context.EntitySet.Where(x=> x.column > EfUtil.ResultOf(GetCurrentFilter(state)));

由于GetCurrentFilter没有作为查询一部分的参数,因此从技术上讲,如果EF Linq可以支持它,则应该可以做到这一点.我怀疑我只是缺少正确的语法.

Since GetCurrentFilter does not have parameters that is a part of the query this should be technically possible to do that if EF Linq can support it that is. I'm suspecting that I'm just missing the correct syntax for that.

推荐答案

使GetCurrentFilter为一个(只读)属性,而不是方法.与方法不同,EF将评估属性的值,而不是尝试将其转换为SQL.

Make GetCurrentFilter a (read only) property instead of a method. EF will evaluate properties to their values, rather than trying to translate them into SQL, unlike methods.

您仅有的另一条路是遍历整个表达式树,搜索您的ResultOf方法的用法,将其参数评估为一个值,然后将该值内联到ResultOf调用曾经所在的位置,重新构建围绕该值的查询.

The only other road that you have is to traverse the entire expression tree, search for usage of your ResultOf method, evaluate its parameter to a value, and then inline that value where the ResultOf call once was, rebuiding the query around that value.

为此,这意味着您不仅需要将要内联的代码包装在对EfUtil.ResultOf的调用中,而且还意味着对查询本身调用一个方法以强制其返回并求值.它:

In order for this to work it means you need to not only wrap the code you want to inline in a call to EfUtil.ResultOf, but it also means calling a method on the query itself to force it to go back and evaluate it:

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)并将结果作为常量内联到查询中.

It would then evaluate GetCurrentFilter(state) on the client side and inline the result as a constant into the query.

作为一个稍微简单的测试,我们可以编写以下代码:

As a slightly simpler test, we can write the following:

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))

System.Int32[].Where(x => (x > 2))

这正是我们想要的.

请注意,不能在调用EfUtil.ResultOf的任何地方使用lambda参数(在这些示例中为x),否则代码将无法正常工作,并且可能无法正常工作(尽管我们如果我们足够注意的话,可能会生成更好的错误消息.

Note that the use of the lambda's parameter (x in these examples) cannot be used anywhere within the call to EfUtil.ResultOf or the code won't work, and couldn't possibly be made to work (although we could generate a better error message if we cared enough).

这篇关于有没有办法将外部函数内联到EF Linq查询中?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆