.NET LINQ IQueryable:`Expression` 有什么用?

.NET LINQ IQueryable: what is `Expression` for?

我正在开发 LINQ 提供程序,因此正在实施 IQueryable。

此界面中的Expression 属性 的用途是什么?我通常只是从我的实现中 return 类似 Expression.Constant(this) 的东西,但不知道这是否不好。

奇怪的是 documentation 声明 "This allows the framework to differentiate between LINQ and Entity SQL queries"。

IQueryable.Expression 成员通过各种 Queryable.* "operators" 反馈给 IQueryProvider (.Where(), .Select(), .Join(), ...), 比如:

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

(摘自referencesource

通常应该是整个表达式。

很明显,如果你通过 Expression.Constant() 直接传递对 IQueryable class 的完整引用,没有人会杀了你,但我确实认为这不是 "kosher".

将 "real" 表达式放入 Expression 的要点(就像 Enumerable.AsQueryable()、EF 和 LINQ-to-SQL 所做的那样,只是命名三个 IQueryable 提供者)是其他外部 classes 可以自由分析和操作 Expression 并以与 Queryable.Where 相同的方式将其反馈给提供者, 这样做

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

举个例子...有一些 "query fixer" 修改查询,如 https://github.com/davidfowl/QueryInterceptor (a generic modifier for queries), or 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 class的新实例方法。 Queryable.* 运算符也会反馈(因为 CreateQuery<>source.Provider 的实例方法)