.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; }
}
(FirstName
和 LastName
映射到数据库)。 DelegateDecompiler 从 IQueryable
中获取 Expression
,搜索具有 Computed
属性的属性,反编译它们并将反编译代码(转换为表达式树)回到 IQueryable.Expression
(尽管使用 IQueryable.Provider.CreateQuery()
)
如果要保存额外的数据,可以放在Provider
中:可以在CreateQuery
中生成IQueryProvider
class的新实例方法。 Queryable.*
运算符也会反馈(因为 CreateQuery<>
是 source.Provider
的实例方法)
我正在开发 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; }
}
(FirstName
和 LastName
映射到数据库)。 DelegateDecompiler 从 IQueryable
中获取 Expression
,搜索具有 Computed
属性的属性,反编译它们并将反编译代码(转换为表达式树)回到 IQueryable.Expression
(尽管使用 IQueryable.Provider.CreateQuery()
)
如果要保存额外的数据,可以放在Provider
中:可以在CreateQuery
中生成IQueryProvider
class的新实例方法。 Queryable.*
运算符也会反馈(因为 CreateQuery<>
是 source.Provider
的实例方法)