LINQ那些事儿(4)- Query Expression和Query Operator

我学习LINQ的时候是直接看MSDN和LINQ team的blog,经常会被里面的一些名词弄混,下面这些名词你都弄懂了吗?

Expression Tree

Expression

Lambda Expression

Query Expression

Query Operator

Expression Tree

和Expression的区别类似XmlNode和XmlElement的区别。Expression Tree用于表达对IQueryable<T>类型数据源的查询树,是Select/Where/From等多个Query method嵌套,在运行时LINQ2SQL会根据Expression Tree来生成SQL语句。

Expression

确切的说是Expression类,为Expression Tree的每一个节点的基类,并提供了构造不同类型Expression的factory method。在System.Linq.Expression命名空间中提供了多种类型的Expression,经常用到的包括:

Class

Description

BinaryExpression

用来表达所有的二元运算,形式为(left) op (right)。如a+b, c && d等。

UnaryExpression

用来表达所有的一元运算,形式为op(operand)。如!a,b++等。

ConstantExpression

用来表达常量或外部变量(不在Expression Tree的控制结构内定义的变量)的定义。

ParameterExpression

用来表达Expression Tree控制结构内的局部变量的定义

MethodCallExpression

用来表达函数的调用

MemberExpression

用来表达属性的访问

让我们用尝试构造Expression Tree来表达

context.Customers.Where(c => c.City == “London”)

// context.Customers
ConstantExpression customersExpr = Expression.Constant(context.Customers);
// 定义Customer c
ParameterExpression parameterExpr = Expression.Parameter(typeof(Customer), "c");
// 访问c.City属性?
MemberExpression cityExpr = Expression.Property(parameterExpr, "City");
// c.City == "London"
BinaryExpression equalExpr = Expression.Equal(cityExpr, Expression.Constant("London"));
// c => c.City == "London"
LambdaExpression conditionExpr = Expression.Lambda(equalExpr, parameterExpr);

// 注意: Where方法的签名是:
// Queryable.Where<T>(this IQueryable<T> source, Func<T, bool> predicate)
MethodCallExpression methodExpr = Expression.Call(
typeof(Queryable), 
     "Where", 
     new Type[] {typeof(Customer)}, 
     customersExpr, conditionExpr
);

Console.WriteLine(methodExpr.ToString());
// 输出:Table(Customer).Where(c => (c.City = "London"))

另外一个有趣的是,在Queryable的源代码里有不少Expression.Quote()的调用,把a变成’(a)是什么意思? 来看看下面这段代码,分别表示() => 2 + 3和 () => ‘(2 + 3):

BinaryExpression expr = Expression.Add(
Expression.Constant(2), Expression.Constant(3));
var expr1 = Expression.Lambda<Func<int>>(expr);
var expr2 = Expression.Lambda<Func<BinaryExpression>>(Expression.Quote(expr));

Console.WriteLine(expr1.Compile()());  // 输出:5
Console.WriteLine(expr2.Compile()());	 // 输出:(2 + 3)

Quote(A)的意思是,输出值为A表达式,而不是A表达式的计算值。

Lambda Expression

lambda expression可以是Expression Tree的一个节点,可以用来创建一个委托。但你知道如何用lambda expression来创建节点和委托吗?

LambdaExpression expr = () => 2 + 3; 	// 错误
LambdaExpression expr = Expression.Lambda(
Expression.Add(Expression.Constant(2), Expression.Constant(3)))	// 正确,定义节点

Expression<Func<int>> expr = () => 2 + 3;	//正确,定义节点
var expr = Expression.Lambda<Func<int>>(
Expression.Add(Expression.Constant(2), Expression.Constant(3)))	// 正确,定义节点

Func<int> fn = () => 2 + 3;		// 正确,定义委托
Func<int> fn = () => { return 2 + 3; }	//正确,定义委托

这里问个问题,你认为下面的语句1和语句2会导致查询操作有什么区别(输出结果都是6)?答案在“LINQ那些事(8)”。

var context = GenerateContext();
Expression<Func<Customer, bool>> condition = c => c.City == "London";

var result1 = context.Customers.Where(condition);				// 1
var result2 = context.Customers.Where(condition.Compile());		// 2

Console.WriteLine(result1.Count());
Console.WriteLine(result2.Count());

Query Operator

指的是在Enumerable和Queryable类中定义的用于用于对数据进行project/filter操作等的extension method,包括Where/Select/Join/OrderBy/GroupBy等。

Query Expression

刚接触LINQ的时候感觉最特别就是可以用类sql的语句在csharp代码里写查询语句

var result =
from c in context.Customers
where c.City == "London"
select c; 

VS对Query expression提供了诸多支持,包括intelligent sense和编译检查等等。但select和where在IL里是没有,只是compiler在运行时根据query expression转化成对Query operator的调用。但是,并不是所有的query operator在query expression有等价表达,query expression支持的operator只有:

Select/SelectMany/Join/GroupJoin/Where/OrderBy/OrderByDescending/GroupBy/ThenBy/ThenByDescending。

既然query operator是通过extension method的方式提供,这就意味着我们可以通过自己编写extension method,来改变query expression的行为,我们看下面的代码:

public static class QueryOperatorExt
    {
        public static IQueryable<T> Where<T>(
            this IQueryable<T> source, Expression<Func<T, bool>> predicate)
        {
            Console.WriteLine("Calling Where in the customer extension method");

            return Queryable.Where(source, predicate);
        }
}
class Program
    {
        static void Main(string[] args)
        {
            var context = GenerateContext();
            var result =
                from c in context.Customers
                where c.City == "London"
                select c;

            Console.WriteLine(result.Count());
        }
}
// 输出:
Calling Where in the customer extension method
6

参考资料

LINQ和DLR的Expression Tree http://rednaxelafx.javaeye.com/blog/237822

Lambda表达式 http://msdn.microsoft.com/zh-cn/library/bb397687.aspx

表达式目录树 http://msdn.microsoft.com/zh-cn/library/bb397951.aspx

总结:本节讨论了Expression Tree/Expression/Lambda Expression/Query expression/Query Operator的含义和区别,示例了如何构建Expression Tree,正确使用lambda expression,以及如何通过extension method来扩展query expression。

链接

LINQ那些事(总)

1、 LINQ那些事儿(1)- 定义从关系数据库到Entity Class的映射

2、 LINQ那些事儿(2)- 简单对象的CRUD操作和Association的级联操作

3、 LINQ那些事儿(3)- 事务和并发冲突处理

4、 LINQ那些事儿(4)- Query Expression和Query Operator

5、 LINQ那些事儿(5)- 动态查询

6、 LINQ那些事儿(6)- DataContext的对象生命周期管理

7、 LINQ那些事儿(7)- 通过自定义IEnumerable<T>来扩展LINQ

8、LINQ那些事儿(8)- 通过自定义IQueryable<T>和IQueryableProvider来扩展LINQ

posted @ 2010-01-01 00:00  海南K.K  阅读(3030)  评论(2编辑  收藏  举报