LINQ本质(一) 映射
2008-08-18 18:27 Ivony... 阅读(4751) 评论(15) 编辑 收藏 举报在这一篇文章里,我不打算探讨任何LINQ的使用技巧或者实现方法,因为这些,有太多太多的资料。我只打算用一篇文章简单的对LINQ的设计思想做一些阐述。如有错漏之处,请不吝指教。
什么是LINQ?
LINQ是这么一组技术,它使得面向对象的程序设计语言(如C#、VB)具备直接查询关系型数据的能力。
如果想要让程序设计语言可以直接查询关系型数据,首先就要将关系型数据映射为程序设计语言认识的东西。
我们先来温习一下关系型数据是什么。
关系型数据的核心是“关系”,这个“关系”是一个集合论中的概念。关于集合论这一块,我就不多说了,有兴趣的朋友可以自己买本离散数学去啃。简单的说,关系就是同构的有序N元组(以下简称序列)的集合,那么序列是什么呢?序列就是N个元素按照指定的顺序的一个排列。什么叫做同构呢?比如说有集合A、B、C,a1,a2...an ∈ A,b1,b2...bn ∈ B,c1,c2...cn ∈ C,那么<a1,b1,c1>和<a2,b1,c2>就是同构的,而<a1,b1>和<b1,a1>以及<a1,b1,c1>和<a1,b1>就不是同构的。
注意,这里为了简化描述,我们将其中一些概念进行了转换。这里的定义并不是严谨的离散数学定义,只是为了下文叙述方便而简单的描述一下,精确的数学定义请自行查阅数学资料。同构是为了叙述方便而自行发明的概念。
所谓关系型数据,我的理解就是,以有限关系这种形式存在的数据,就是关系型数据。
所以,关系型数据,指的是一种数据的存在结构。比如说关系型数据库,就是以关系型数据为储存结构的数据库。无论这个数据最终是否可以以XML或者其他非关系形式输出,只要数据的储存和检索是关系形式的,就是关系型数据库。
从这一点出发,XML的节点集合,是一种关系型数据,任意类型的列表或数组,也是一种关系型数据。一个目录下的所有文件的信息,同样是一个关系型数据。
在关系型数据库中,我们一般将关系称呼为数据表,而序列则是记录。
LINQ将关系型数据中的关系(数据表)映射为.NET Framework的IEnumerable接口,而序列(记录)则映射为对象。因为关系是同构的序列的集合,所以一个关系的所有序列都是同构的,这个可以映射为类。所以关系就是一个强类型的IEnumerable,即IEnumerable<T>,不过LINQ也可以对非强类型的IEnumerable进行查询,但不在本文范围内。
值得注意的一点是,关系可以被映射成IEnumerable<T>,但反之,并不是IEnumerable<T>都可以代表一个关系。关于IEnumerable<T>的特性,敬请期待“装配脑袋”大牛为我们解答,小弟就不献丑了。
下文中为了叙述方便并且避免数据库中心化,一律将关系称呼为序列集合。
上面我们简单的讲了关系型数据的映射,或者说关系型数据在.NET Framework中的体现。下面我们谈谈关系型数据的查询的映射。
我们通常使用SQL语句来对关系型数据进行查询,一个简单的SQL语句如下:
SELECT Music.Name AS MusicName, Artist.Name AS ArtistName, Artist.Gender AS ArtistGender FROM Music INNER JOIN Artist ON ( Music.ArtistID = Artist.ID ) WHERE Music.Genre = '流行音乐' AND Artist.Contry = '中国';
其实,每一个SQL语句都包含很多的子操作,而SQL语句的最终结果就是这些子操作串联执行的最终结果。上面这个SQL我们可以将其拆分为三个子操作:
1、连接(join),将Music序列集合和Artist序列集合进行连接。
2、筛选(where),在连接的结果中筛选出满足条件“Music.Genre = '流行音乐' AND Artist.Contry = '中国'”的元素。
3、映射(select),将筛选后的结果数据的元素进行重新组织最终输出成<MusicName,ArtistName,ArtistGender>这样结构的序列集合。
上述SQL的执行结果其实就是按顺序执行“连接 -> 筛选 -> 映射”三个子操作结果。
其实所有的SQL都是由这样的一些子操作构成的,我们将构成SQL的这些子操作做一个简单的描述:
1、筛选(where),筛选操作用于删去集合中不符合要求的元素形成一个新的集合,但不能改变元素本身。
2、映射(select),映射操作用于对集合中每一个元素进行运算,运算结果将形成一个新的集合。筛选操作不能改变集合元素,但可以从集合中删除元素。映射操作不能从集合中删除或者增加元素,但可以对元素进行改变。
3、分组(group by),分组操作用户将关系中相似的元素组合成N个新的集合,这些集合的集合即是分组操作的输出。在LINQ中,分组的结果集的所有元素都是IGrouping<TKey, TElement>类型的。
4、连接(join),连接操作将两个集合的元素合并在一起,并按照某个条件进行有限的筛选形成一个新的序列集合。在现在的标准SQL中,两个集合的连接可以分为五种情况,分别是内连接(INNER JOIN),左外连接(LEFT OUTER JOIN),右外连接(RIGHT OUTER JOIN),全外连接(FULL OUTER JOIN)和交叉连接即笛卡尔积(CROSS JOIN)。事实上,除了交叉连接外,其他连接的结果都是交叉连接结果的一个筛选结果(外连接可以认为是将两个集合都加入一个“空”元素之后的笛卡尔积的结果的筛选)。
5、排序(orderby),其实排序操作是一个很特殊的操作,熟悉集合的朋友都知道,集合中的元素是不存在顺序的,所以排序操作的结果不是集合,.NET使用了一个新的类型IOrderedEnumerable<TElement>来与没有排序的真序列集合类型IEnumerable<T>进行区别(当然IEnumerable<T>本身的确也是有序的,这样做可能是为了语义方面的考虑)。
几乎所有的SQL大都可以分解为上述五种操作的串联。何谓串联,上面所有的子操作输入都是一个集合,输出也是一个集合。串联就是指每一个子操作的输入都是上一个子操作的输出,这样串起来执行。
最后,我们再将这些操作封装成函数,则灵活多变的SQL语句就可以用函数的串联调用来表示。
除了这五个最基本的操作外,LINQ还提供了一些特定组合,如连接映射,分组连接等……在此不赘述。
在下一篇文章中,我将对LINQ语法进行一个阐述。
关于文中出现的一些概念的参考资料:
关系数据库,是建立在关系模型基础上的数据库,借助集合代数等数学概念和方法来处理数据库中的数据。现实世界中的各种实体以及实体之间的各种联系均用关系模型来表示。
关系模型的基本假定是所有数据都表示为数学上的关系,就是说n个集合的笛卡儿积的一个子集,有关这种数据的推理通过二值(就是说没有NULL)的谓词逻辑来进行, 这意味着对每个命题都没有两种可能的求值: 要么是真要么是假。数据通过关系演算和关系代数的一种方式来操作。
资料来源:维基百科。顺便说个好消息,中文维基最近解封了。