LINQ之路(2):LINQ to SQL本质

在前面一篇文章中回顾了LINQ基本语法规则,在本文将介绍LINQ to SQL的本质。LINQ to SQL是microsoft针对SQL Server的一个ORM的解决方案。既然是ORM,那对象(Object)、关系数据(Relation)和映射(Mapping)这三个组成要素是不可或缺的。本文将分为对象和关系数据的映射、从查询表达式到SQL的转换和DataContext数据上下文三个部分来说明。

1.对象和关系数据的映射

通过定义对象和关系数据的映射关系,我们可以以面向对象的方式来处理关系数据。通常,定义对象和关系数据的映射有两种方式:一是自定义特性,二是XML描述。前者是通过自定义一些描述关系数据结构的Attribute来修饰类,从而建立映射关系,而后者则是通过XML来描述这种映射关系。在LINQ to SQL中,为了简化工作,微软为我们提供了非常好用的工具SqlMetal,利用它,我们可以为 LINQ to SQL 的 .NET Framework 组件生成代码和映射。例如,我们可以利用

C:\Program Files (x86)\Microsoft Visual Studio 11.0\VC>SqlMetal /conn:"server=.;
database=IT_Company_New;UID=sa;Password=123456" /language:csharp /namespace:jell
o.test /code:d:\CompanyDataContext.cs

来生成数据上下文代码,我们也可以利用

C:\Program Files (x86)\Microsoft Visual Studio 11.0\VC>SqlMetal /conn:"server=.;
database=IT_Company_New;UID=sa;Password=123456" /dbml:d:\IT_Company_new.dbml

来生成中间数据库标记语言(.dbml)文件。

其实,这也是LINQ to SQL设计器采取的方式。

通过LINQ to SQL设计器将会为我们生成一下三个文件:

  1. .dbml文件:中间数据库标记语言文件,描述映射,由SqlMetal生成
  2. .dbml.layout文件:设计器界面布局文件,描述组件位置
  3. .designer.cs文件:生成代码部分,由SqlMetal生成

这里详细介绍下生成的代码,代码大致分为两个部分:数据上下文和表对象。数据上下文对应数据库,表对象。数据上下文继承自System.Data.Linq.DataContext,表对象继承自INotifyPropertyChanging, INotifyPropertyChanged。数据上下文采用AttributeMappingSource来指定映射方式,除了AttributeMappingSource方式还可以采用XmlMappingSource,使用DatabaseAttribute修饰以映射数据库,表对象使用TableAttribute修饰以映射表。数据上下文包含表对应的Table属性以及一些CURD可扩展性方法定义,表对象包含由表字段生成的属性(由ColumnAttribute和AssociationAttribute修饰以映射表字段)以及一些OnLoaded、OnValidate、OnCreated、On×××Changing和On×××Changed可扩展性方法定义。数据上下文可能还会包含存储过程及自定义函数的实现。数据上下文默认采用AttributeMappingSource来指定映射方式,相关的Attribute可参见MSDN

2.将查询表达式转换为SQL

var query = from student in _dataContext.T_Student
where student.Age < 20
select student;
foreach (var student in query)
{
Console.WriteLine(student.Name);
}

上面的查询表达式可以从数据库中检索出年龄小于20岁的学生。那么它是如何工作的呢?

这和我们上一篇中的写法几乎完全一样,唯一的区别是上一篇中数据源是IEnumerable或List类型的,它们继承自IEnumerable接口,而这里的数据源是Table类型的,它继承自IQueryable接口,而IQueryable继承自IQueryable,IQueryable又继承自IEnumable接口,这里真正起作用的是IQueryable接口,定义如下:

public interface IQueryable : IEnumerable
{
Type ElementType { get; }
Expression Expression { get; }
IQueryProvider Provider { get; }
}

包含三个属性:

ElementType:包含元素类型

Expression:要执行的操作的Expression

Provider:实现了IQueryProvider接口的Provider

再来看下IQueryProvider接口定义:

public interface IQueryProvider
{
IQueryable CreateQuery(Expression expression);
IQueryable<TElement> CreateQuery<TElement>(Expression expression);
object Execute(Expression expression);
TResult Execute<TResult>(Expression expression);
}

包含四个方法,CreateQuery和Execute分别一个泛型和一个非泛型方法。

  • CreateQuery:构造一个IQueryable或IQueryable对象,该对象可计算指定表达式目录树所所示的查询。

  • Execute:执行指定表达式目录树所表示的查询。

通过这种接口的设计,IQueryable允许我们创建支持其它非SQL Server数据库的实现。Provider将借助于IQueryable所提供的种种信息把查询语句转换为另一种方式,转换的实际操作将由CreateQuery方法实现,随后转换的结果将由Execute方法使用。

LINQ使用哪种方式进行转换,取决于数据源类型。若数据源未实现IQueryable或IQueryable接口而实现了IEnumerable或IEnumerable接口,则一般是通过是Enumerable的扩展方法转换成在内存中的操作的,而若实现了IQueryable或IQueryable接口,则一般是通过Queryable的扩展方法转换成表达式目录树。下面以一个简单的例子来说明它的转换流程:

var query = from student in _dataContext.T_Student
where student.Age < 22
let Len = student.Name.Length
orderby Len
select student;
foreach (var student in query)
{
Console.WriteLine(student.Name);
}

步骤如下:

1.查询表达式转换为扩展方法:

var query = _dataContext.T_Student.Where(student => student.Age < 22)
.Select(student => new { Len = student.Name.Length, student = student })
.OrderBy(student => student.Len)
.Select(student => new
{
ID = student.student.ID,
Name = student.student.Name,
Age = student.student.Age,
ClassID = student.student.ClassID
});

2.生成表达式目录树,通过调用Queryable一系列的扩展方法构造IQueryable对象。在扩展方法中通过source.Provider.CreateQuery方法来构造当前表达式目录树。如下图所示:

3.在进行迭代等真正获取数据操作中调用source.Provider.Execute方法来执行表达式以获取结果。这个过程比较复杂,在Execute方法中,.net framework是通过ExpressionVisitor类以Visitor模式方式来将表达式目录树解析,根据AttributeMapping或XmlMapping映射规则生成如下SQL,然后执行该SQL。

SELECT [t1].[ID], [t1].[Name], [t1].[Age], [t1].[ClassID]
FROM (
SELECT LEN([t0].[Name]) AS [value], [t0].[ID], [t0].[Name], [t0].[Age], [t0].[ClassID]
FROM [dbo].[T_Student] AS [t0]
) AS [t1]
WHERE [t1].[Age] < @p0
ORDER BY [t1].[value]

3.DataContext数据上下文

在LINQ to SQL中,DataContext是一个不能不说的东东。它拥有这么几个功能:管理业务实体、管理数据库连接、数据库映射、查询转换、对象标识和跟踪变化。在DataContext中维护着Table这些业务实体,可以很方便地这些业务实体进行操作。对于数据库连接的管理,DataContext是通过IProvider凭借IConnectionManager来进行管理的。LINQ to SQL默认使用的AttributeMapping来进行数据库映射的,通过DatabaseAttribute映射某一具体数据库,通过TableAttribute映射表,通过ColumnAttribute映射表字段。DataContext是通过借助于Table来实现将查询表达式转换为SQL的。DataContext通过CommonDataServices进行对象标识和变化跟踪的,在CommonDataServices对象中有IdentityManager和ChangeTracker两个属性,前者用于对象标识,后者用于变化跟踪。

每次查询数据库时,DataContext都会使用IdentityManager来判断是否已有某个对象的缓存,若已缓存则DataContext从内部缓存中获取。

在修改业务实体时,DataContext将会使用ChangeTracker同时保留修改前后的两个值,当提交修改过的记录时才会向数据库发出请求。

LINQ之路(2):LINQ to SQL本质的更多相关文章

  1. LINQ之路10:LINQ to SQL 和 Entity Framework(下)

    在本篇中,我们将接着上一篇“LINQ to SQL 和 Entity Framework(上)”的内容,继续使用LINQ to SQL和Entity Framework来实践“解释查询”,学习这些技术 ...

  2. LINQ之路 4:LINQ方法语法

    书写LINQ查询时又两种语法可供选择:方法语法(Fluent Syntax)和查询语法(Query Expression). LINQ方法语法是非常灵活和重要的,我们在这里将描述使用链接查询运算符的方 ...

  3. LINQ之路系列文章导读

    本系列文章将会分为3篇来进行阐述,如下: LINQ之路(1):LINQ基础 LINQ之路(2):LINQ to SQL本质 LINQ之路(3):LINQ扩展

  4. LINQ之路15:LINQ Operators之元素运算符、集合方法、量词方法

    本篇继续LINQ Operators的介绍,包括元素运算符/Element Operators.集合方法/Aggregation.量词/Quantifiers Methods.元素运算符从一个sequ ...

  5. LINQ之路 7:子查询、创建策略和数据转换

    在前面的系列中,我们已经讨论了LINQ简单查询的大部分特性,了解了LINQ的支持计术和语法形式.至此,我们应该可以创建出大部分相对简单的LINQ查询.在本篇中,除了对前面的知识做个简单的总结,还会介绍 ...

  6. LINQ之路(3):LINQ扩展

    本篇文章将从三个方面来进行LINQ扩展的阐述:扩展查询操作符.自定义查询操作符和简单模拟LINQ to SQL. 1.扩展查询操作符 在实际的使用过程中,Enumerable或Queryable中的扩 ...

  7. LINQ之路16:LINQ Operators之集合运算符、Zip操作符、转换方法、生成器方法

    本篇将是关于LINQ Operators的最后一篇,包括:集合运算符(Set Operators).Zip操作符.转换方法(Conversion Methods).生成器方法(Generation M ...

  8. [转]LINQ之路系列博客导航

    分享一个学习Linq的好博客:Linq之路

  9. LINQ之路(1):LINQ基础

    本文将从什么是LINQ(What).为什么使用LINQ(Why)以及如何使用LINQ(How)三个方面来进行说明. 1.什么是LINQ LINQ(Language Integrated Query)是 ...

随机推荐

  1. Java8高中并发

    Java8中学并发 本文翻译自:http://jaxenter.com/lean-concurrency-in-java-8-49924.html 转载请注明出处:http://blog.csdn.n ...

  2. 牛逼的验证码,printf返回值

    牛逼的验证码,如下图, 结果是4321,为什么呢,主要是printf返回值问题?那么printf到底返回什么? 经查阅,printf的返回值是打印的字符个数,因此结果是4321就很明显了.

  3. 使用windows-SQLyog连接linux-mysql

          嘿嘿,最近又清闲了一点,重新安装了mysql去学习.   -----博客园-邦邦酱好 系统环境: 1. 主机为windows系统,安装了SQLyog. 2. 主机上还安装了虚拟机,系统为c ...

  4. c#开发微信公众平台

    之前帮公司开发过微信公众账号,今天特别将过程再回顾记录下来. 1.URL配置 启用开发模式需要先成为开发者,而且编辑模式和开发模式只能选择一个,进入微信公众平台-开发模式,如下: 从上面可以看出,点击 ...

  5. 所有城市list每次从页面花1段时间抽取后写入到数组,

    所有城市list每次从页面花1段时间抽取后写入到数组,

  6. 解决.net的堆碎片化带来的内存占用过大的问题

    场景 使用WCF开发的服务端在多个客户端登录后,其服务器的内存占用不断增加. 分析 使用Windbg分析得到内存碎片化严重,其中包含了非常大的空闲空间,最大的一块竟然有150M,真正使用的空间其实很小 ...

  7. 【ALearning】第四章 Android Layout组件布局(一)

    在本章中,我们将Android学习组件布局.在前面的章节,我们也开始使用LinearLayout布局.然后我们在布局文件更加具体的学习和理解,会. Android的界面是有布局和组件协同完毕的,布局好 ...

  8. Java贪吃蛇游戏

    package snake.game; import java.awt.CardLayout; import java.awt.Color; import java.awt.Graphics;   i ...

  9. mac开启22port

    mac开启22port 选择System prefrence -> sharing , 将remote login打开 測试是否打开 import socket s = socket.socke ...

  10. 9种CSS3 blend模式制作的鼠标滑过图片标题特效

    这是一款使用CSS3 background-blend-mode制作的鼠标滑过图片标题特效.该图片标题特效在鼠标滑过一张图片的时候,图片的标题会对应的动画,而且图片会使用css blend模式渲染为很 ...