.NET LINQ IQueryable:`Expression` 的作用是什么?

3
我正在开发一个LINQ提供程序,所以正在实现IQueryable接口。
在这个接口中,Expression属性的目的是什么?我通常从我的实现中返回类似于Expression.Constant(this)的东西,但不知道这是否有问题。
奇怪的是,文档指出:“这允许框架区分LINQ和Entity SQL查询”。

尝试查看该属性中EF或LINQ-to-SQL中存在什么... - xanatos
1
据我记得,Expression 属性保存当前 IQueryable 的表达式,因为我们可以有一系列的 IQueryable,所以每个 IQueryable 都有它自己和之前分组的表达式。但是我可能错了,因为我很久以前使用过它。 - Sergey Litvinov
@SergeyLitvinov 是的,我知道这是一种可能的使用方式,但据我所知,框架并没有要求这样使用它。那么这个属性是供实现者任意使用的吗? - Ian Newson
1个回答

5

IQueryable.Expression成员由所有不同的Queryable.*“操作符”(.Where(), .Select(), .Join(), ...)反馈给IQueryProvider

public static IQueryable<TSource> Where<TSource>(this IQueryable<TSource> source, Expression<Func<TSource, bool>> predicate) {
    if (source == null)
        throw Error.ArgumentNull("source");
    if (predicate == null)
        throw Error.ArgumentNull("predicate");
    return source.Provider.CreateQuery<TSource>( 
        Expression.Call(
            null,
            ((MethodInfo)MethodBase.GetCurrentMethod()).MakeGenericMethod(typeof(TSource)), 
            new Expression[] { source.Expression, Expression.Quote(predicate) }
            ));
}

(摘自参考来源

通常应该是整个表达式。

显然,如果您直接通过Expression.Constant()将整个引用传递给您的IQueryable类,没有人会杀了你,但我认为这不是“正当的”。

Expression中放置“真实”表达式的重点(就像Enumerable.AsQueryable(),EF和LINQ-to-SQL所做的那样,只是举几个IQueryable提供程序的例子)是其他外部类可以自由地分析和操作Expression并以与Queryable.Where相同的方式将其反馈给提供程序,因此执行:

Expression expression = source.Expression;
// here "expression" is manipulated
return source.Provider.CreateQuery<SomeType>(expression);

给出一个例子...有一些“查询修正器”可以修改查询,比如https://github.com/davidfowl/QueryInterceptor(一个通用的查询修正器),或者https://github.com/hazzik/DelegateDecompiler,它允许执行以下操作:
var employees = (from employee in db.Employees
                 where employee.FullName == "Test User"
                 select employee).Decompile().ToList();

当数据库中没有映射FullName属性时,它就像一个属性:

class Employee
{
    [Computed]
    public string FullName
    {
        get { return FirstName + " " + LastName; }
    }

(其中FirstNameLastName映射到数据库)。DelegateDecompilerIQueryable中获取Expression,搜索具有Computed属性的属性,对其进行反编译,并将反编译的代码(转换为表达式树)放回IQueryable.Expression(通过使用IQueryable.Provider.CreateQuery())。
如果您想保存其他数据,可以将其放入Provider中:您可以在CreateQuery方法中生成IQueryProvider类的新实例。这也由Queryable.*运算符反馈(因为CreateQuery<>source.Provider的实例方法)。

感谢您详细的回答。作为一个小的后续问题,使用Expression属性尝试修改、复制或检查查询是否相对常见?我从未在实践中看到过这样做,我会认为这是一个非常不知名的“功能”。 - Ian Newson
哦,我的实现确实公开了其他属性,允许检查配置的查询,因此 Expression.Constant(this) 看起来并不像最初那么愚蠢。 - Ian Newson
@IanNewson 我会说这在程序员中很罕见,在库编写者中不太常见。 - xanatos

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接