posts - 21, comments - 96, trackbacks - 8, articles - 0

LINQ本质(再版)第一篇 关系

Posted on 2008-08-28 14:35 Ivony... 阅读(977) 评论(6)  编辑 收藏 网摘 所属分类: .NETLINQ

在这一篇文章里,我不打算探讨任何LINQ的使用技巧或者实现方法,因为这些,有太多太多的资料。我只打算用一篇文章简单的对LINQ的设计思想做一些阐述。如有错漏之处,请不吝指教。

 

这是这篇文章的再版,在上一版发布后,大家给了我充分的反馈意见,使我意识到那一篇文章中哪些知识点需要进一步加强,我本来打算再另外写两篇文章来做一个补完计划。但是从方便阅读的角度考虑,以及上一版本在篇幅上的确是足够精悍,所以决定在那个基础上进行增补,也就是本文。不过我会继续倾听大家的反馈意见并不断修改,不排除再再版的可能。

 

 

 

 

什么是LINQ?

LINQ是这么一组技术,它使得面向对象的程序设计语言(如C#、VB)具备直接查询关系型数据的能力。

 

 

 

这里的关系型数据,是我给下的一个定义,指的是,所有以数学中定义的“关系”的形式表示的数据。

我们来温习一下关系的概念,“关系”是一个离散数学中的概念。离散数学中对关系的定义是,若A、B是两个集合,则A×B的每一个子集都是A×B的一个关系(A×B即是集合A与B的笛卡尔积的意思)。

如果对笛卡尔积和集合的定义不够了解的话,请自行购买离散数学的书籍补习,本文假定读者都具备离散数学的基本知识。这是关系型数据的基础。

 

我们不妨花些时间来做一个科普些的解释,假设集合A是所有男人的集合,集合B是所有女人的集合。那么会存在一个关系比如说夫妻关系R。显而易见,R必然是A×B的一个子集。

这种在现实世界中有意义的关系,如男人集合和女人集合中的夫妻关系。可能比较好理解,但是离散数学中的“关系”的定义,却不一定要求这个“关系”有什么实际意义。就像集合的定义也不要求集合的元素有什么实际的意义一样。在世界上所有有实际意义的关系,都是离散数学中的关系,反之不然

再来说说关系型数据,关系型数据基于这么一个假定:即所有数据都表示为离散数学上的关系

我们不妨拿一个典型的数据形式:一个XML数据来演练一下。为了简化问题,我们使用一个只有元素内容没有文本内容的XML,同时,我们也认为这个XML元素的先后次序与其表达的数据没有关系(即我们认为<a ...></a><b ... />和<b ... /><a ...></a>表达的数据是一样的,元素a与b的前后顺序与数据没有关系)。

首先,XML文档是由一个根元素(节点)以及其所有的子孙元素(节点)构成的,显然这些元素之间存在一个隶属关系(父级关系)。令这个XML的所有元素的集合为N,则这个元素隶属关系就是在N×N的一个关系。其次,XML的元素节点其实是由一个元素名(TagName)和任意个属性构成的,所以XML的所有元素节点都可以用一个元素名与属性集合的关系。最后,属性也是属性名与属性值的一个关系。

 

由上我们可以得出,这个XML文档其实可以由下列关系所表示:

1、属性名与属性值的关系,每一个关系项即为一个属性。(属性名集合 × 属性值集合 的一个关系)

2、元素名与属性集合的关系,每一个关系项即为一个元素。(元素名集合 × 属性集合的集合 的一个关系)

3、元素与元素的隶属关系。(元素集合 × 元素集合 的一个关系)

 

完整的示例可以参考另一篇文章:如何将XML数据变成关系型数据(未完成)。

 

当然,我们也完全可以用其他的关系形式来描述。不过这并不重要,重要的是,我们用这些关系来描述了一个XML数据,这个XML数据完全可以用这些关系来复原。

 

 

上面我们谈到了数据怎么用关系来表示,但并不是所有的数据都能被计算机处理的。离散数学上的关系,可以是任何集合的笛卡尔积的子集,但计算机却没法处理任何集合。能够被处理的数据无非是整数、字符串等等。同时,在本篇文章中,我们认为由不同类型数据所组成的集合也是计算机无法处理的。

仅由基础类型集合组成的关系数据,就是能够被现在的关系型数据库所处理的数据。关系型数据库中的所有数据,都是表示为上述“关系”这种形式的,我们一般将“关系”称为数据表。

关系型数据库所不能处理的复合类型关系和包含集合元素的关系(即在集合的集合上的关系)都可以被转化为不包含集合元素以及复合类型的关系,在这里我们不作深入讨论。

LINQ不仅能处理仅由基础类型集合组成的关系数据,也能处理基础类型和复合类型集合组成的关系数据。所以,大多关系型数据库能执行的查询都能被映射成LINQ中的查询,但反之不然。

 

 

最后我们来看一下复合类型数据。我们令集合I = 所有整型数据、集合S = 所有字符串数据。某个复合类型的所有数据就是N个基础类型或复合类型数据集合上的关系,例如class Member { string Name, string Email, int Age }这个复合类型的所有数据就是一个S × S × I上的关系。所以,任何一个复合类型的所有数据都能被表示为多个基础类型数据集合和复合类型数据集合的关系,反之亦然。

不过这里要指出的是,复合类型更多指的是C语言中的结构(struct),而非类(class)。因为类是结构的扩展,所以类具备结构的一切特征,而结构却不具备类的所有特征(封装、继承等),所以,所有的关系能被表示为一个类的所有对象集合,但反之不然。

 

如果您没有看懂上面关于关系型数据的说明,那也没有关系,您只需要记住下面这个结论就可以了。

关系型数据就是所有被表示为关系的数据,所有可以被计算机处理的关系型数据,都能表示为某个类型的所有项(对象)。大多关系型数据库所能处理的关系型数据都是由基础类型所组成的,但LINQ能查询的数据不限于此。

 

记住三个不等可能会对将来分析问题时有所帮助:

a、不是所有表示为关系的数据都能被计算机处理。

b、不是所有能被LINQ执行的查询都能被关系型数据库执行。

c、不是所有对象都是表示为关系的数据。

 

 

关系在.NET Framework中的表现形式。

微软在设计LINQ的时候,并没有为关系而造出一种新的类型。不过由关系的定义我们可以知道,关系就是一种集合,集合是一个抽象的概念,但是.NET Framework有一个最简单的集合访问器:IEnumerable接口(.NET中的所有集合访问器都从此接口继承)。所以,关系数据也可以用IEnumerable来访问。同时,由上面的分析可知,任何一个关系都是某个类型的所有对象的集合。所以,关系就是一个强类型的集合,我们用IEnumerable<T>来在.NET Framework表示一个关系。

 

 

最后的几个容易混淆的点。

a、关系都是集合,集合不都是关系。

b、所有能被计算机处理的关系数据都能被IEnumerablt<T>来表示,但反之不然。当IEnumerable<T>表示的数据并非是一个关系数据时,LINQ查询就会出问题。

c、由于关系都是集合,所以对关系的某些查询与对集合的查询是相同的。但关系比集合能进行更多的查询操作(如连接)。

d、LINQ是面向关系型数据的,不是面向数据库中的表或者是我们所创造的一个数组,也不是面向IEnumerable<T>接口。

 

 

关于文中出现的一些概念的参考资料:

 

关系数据库,是建立在关系模型基础上的数据库,借助集合代数数学概念和方法来处理数据库中的数据。现实世界中的各种实体以及实体之间的各种联系均用关系模型来表示。

关系模型的基本假定是所有数据都表示为数学上的关系,就是说n集合笛卡儿积的一个子集,有关这种数据的推理通过二值(就是说没有NULL)的谓词逻辑来进行, 这意味着对每个命题都没有两种可能的求值: 要么是真要么是假。数据通过关系演算关系代数的一种方式来操作。

 

资料来源:维基百科。顺便说个好消息,中文维基最近解封了。

 

Technorati 标签: ,

Feedback

#1楼    回复  引用  查看    

2008-08-29 22:15 by Cat Chen      
上次别人说Linq to Xml和关系型数据无关,你这次又一定要把Linq to Xml扯到集合论上去,搞那么复杂干什么呢。

Linq就是Linq,你要扯集合论,就应该聚焦到Linq to Sql。你没必要要求人家Linq屈身为Linq to Sql以满足你大标题的要求。

#2楼    回复  引用  查看    

2008-08-29 22:19 by XMLSpy      
真是好文章.
给我补了好多数学知识.

另外就是那个"反之不然"太拗口,应该是反之未然吧 :)

#3楼    回复  引用  查看    

2008-08-29 22:21 by XMLSpy      
@Cat Chen
我认为博主讲解得非常好,把一些原理都讲解出来了.
很多程序上的东西都是以数学为基础的

#4楼 [楼主]   回复  引用  查看    

2008-08-29 22:52 by Ivony...      
--引用--------------------------------------------------
XMLSpy: @Cat Chen
我认为博主讲解得非常好,把一些原理都讲解出来了.
很多程序上的东西都是以数学为基础的
--------------------------------------------------------

正是如此,其实计算机科学是非常理科的,基本的概念如果厘清了,思路正确了,很多问题都是一通百通,迎刃而解。

这也是我为什么要花这么大的力气来写这些不讨好的理论的原因,因为估计我不写,大家就只有等大牛们去翻译老外的文章了。

LINQ作为一个新生的技术,的确在很大程度上可以取代现有的数据访问,但是LINQ也有非常多的局限,而如果你真正理解到LINQ的本质之后,自然就能明白哪些是LINQ能做的,哪些是LINQ不能做的,不会在一些LINQ无法实现的事情或者不该是LINQ处理的事情上上纠缠不清浪费时间。

理解LINQ的设计思想,也有助于我们自己扩展LINQ。

#5楼 [楼主]   回复  引用  查看    

2008-08-29 23:00 by Ivony...      
@Cat Chen
科普的文章有足够多的人去写,复杂的原理写出来费力又不讨好。

上一篇文章我就针对你提出的问题给出了解答,LINQ如果只是让我们可以把SQL写到程序里面,那与我的DbUtility功能上没有二致,顶多是多个语法和类型检查。LINQ没有在CLR上新增任何东西,新增的所有东西都是编译器的语法糖,这些语法糖的文章铺天盖地,我闭着眼睛也能写一大堆。但是这些最多能告诉我们LINQ怎么用,而我这个系列的文章的目的在一开始就说明白了,我没打算告诉大家LINQ怎么用……


我承认这篇文章再版后已经超越了LINQ的范畴,其中一些问题如果对于勤于思考的人来说可以非常有所裨益,比如说怎么样将XML数据转换成关系型数据?这是LINQ to XML的本质。如果理解了这一点,我们完全可以自己发明LINQ to 别的什么数据类型,LINQ to 文件系统?很不错不是么?

#6楼 [楼主]   回复  引用  查看    

2008-08-29 23:01 by Ivony...      
--引用--------------------------------------------------
XMLSpy: 真是好文章.
给我补了好多数学知识.

另外就是那个&quot;反之不然&quot;太拗口,应该是反之未然吧 :)


--------------------------------------------------------


反之未然不如反之不然的否定来的强烈,而这些反之不然都是非常重要的点。这些反之不然就是LINQ不能或者不适合的应用场景。