本文档主要介绍.NET 开发中两项新技术,.NET 平台语言中的语言集成查询技术 -
LINQ,与 ADO.NET 中新增的数据访问层设计技术 ADO.NET Entity Framework。
ADO.NET 的 LINQ to Entity 部分以 LINQ 为基础,为了完整性首先介绍 LINQ 技术。
预备知识
LINQ 技术
LINQ 是.NET 3.5 中新增的一种技术,这个技术扩展了.NET 平台上的编程语言,使其可以更加方便的进行
数据查询,单纯的 LINQ 技术主要完成对集合对象(如 System.Collection 下或 System.Collection.Generic
命名空间下的对象)的查询。结合 LINQ Provider 可以实现对 XML 文件(使用 LINQ to XML – 位于
System.Xml.Linq 命名空间下的类),数据库(可以使用 LINQ to SQL 或下文要详细介绍的 LINQ to Entity)
等对象的操作。
LINQ 是一种运行时无关的技术,其运行于 CLR2.0 之上,微软对 C#3.0 与 VB9.0 的编译器进性扩展,从
而使其可以将 LINQ 编写的程序编译为可以被 CLR2.0 的 JIT 所理解的 MSIL。
LINQ 技术的基础 - C#3.0
自动属性
隐式类型
对象集合初始化器
匿名类
1.
2.
3.
4.
5.
6.
7.
8.
9.
扩展方法
10.
11.
12.
Lambda 表达式
自动属性
这个概念很简单,其简化了我们在.NET 的时候手写一堆私有成员+属性的编程方式,我们只需要使用如下
方式声明一个属性,编译器会自动生成所需的成员变量。
1 public class Customer2 {3
{ get; set; }5 }
public int Id { get; set; }4
public string Name
在我使用 LINQ 完成的项目中,使我了解到自动属性方便的一个用途如下:
在使用 LINQ 获取数据的过程中,我们常常需要使用 select new 语句查询出一个对象(往往是 IEnumerable
类型的)用于数据绑定。在一般情况下如果是直接绑定(如直接将查询结果赋给一个 Gridview 控件的
DataSource 属性)我们可以直接 select new 来返回一个匿名类的对象。如果我们还需要对这个集合对象
进行进一步操作,我们将必须使用 select new class-name 这样的语言返回一个类的对象,大部分情况下
这个类只作为实体的一个结构而不需要完成一些操作操作,这时候使用自动属性来完成这个类将是非常简
洁高效的。
隐式类型
这个名称可能对你很陌生,但是 var 这个关键字应该都用过,在 C#中使用 var 声明一个对象时,编译器会
自动根据其赋值语句推断这个局部变量的类型。赋值以后,这个变量的类型也就确定而不可以再进行更改。
另外 var 关键字也用于匿名类的声明。
应用场合:var 主要用途是表示一个 LINQ 查询的结果。这个结果可能是 ObjectQuery<>或 IQueryable<>
类型的对象,也可能是一个简单的实体类型的对象。这时使用 var 声明这个对象可以节省很多代码书写上
的时间。
对象初始化器与集合初始化器
在.NET2.0 中构造一个对象的方法一是提供一个重载的构造函数,二是用默认的构造函数生成一个对象,
然后对其属性进行赋值。在.NET3.5/C#3.0 中我们有一种更好的方式来进行对象的初始化。那就是使用对
象初始化器。这个特性也是匿名类的一个基础,所以放在匿名类之前介绍。
还是那就话,好的代码强于注释,下面用几个代码段说明初始化器:
(代码出自:李永京的博客 http://lyj.cnblogs.com)
基本用法:
1 User user = new User { Id = 1, Name = "YJingLee", Age = 22 };
嵌套使用:
1 User user = new User 2 { 3
22, 6
= 2100010
Address = new Address 7
}11 };
Id = 1, 4
{ 8
Name = "YJingLee", 5
City = "NanJing", 9
Age =
Zip
类似于对象初始化器初始化一个对象,集合初始化器初始化一个集合,一句话,有了它你就不用在将元素
通过 Add 逐个添加了。仍然给出代码示例:
基本使用:
1 List num = new List { 0, 1, 2, 6, 7, 8, 9 };
结合对象初始化器,我们可以写出如下简洁的代码:
1 List user = new List{2
User{Id=1,Name="YJingLee",Age=22},3
User{Id=2,Name="XieQing",Age=25},4 };
new
new
应用场合:
还是前文提到的 select new class-name 语法,后面可以直接接一个初始化器来将查询结果返回到这个对
象。
匿名类
有了前文初始化器的介绍,匿名类就很简单了。我们可以使
用 new { object initializer } 或 new[]{ object, …} 来初始化一个匿名类或不确定类型的
数组。匿名类的对象需要使用 var 关键字声明。示例代码:
1 var p1 = new { Id = 1, Name = "YJingLee", Age = 22 };
应用场合:
还是同上面的例子提到的当直接使用 select new { object initializer }这样的语法就是将一个 LINQ 查询的
结果返回到一个匿名类中。
扩展方法
扩展方法是 C#中新增的很重要的特性之一。其对于 LINQ 的实现起着关键的作用。在.NET2.0 时代是没有
LINQ 的,所以.NET2.0 以及之前版本中的集合类在设计的时候没有预留用于 LINQ 的方法。为了在不破坏
这个类现有封装的前提下又可以为其添加 LINQ 的支持就需要用到扩展方法。
扩展方法使用上类似于静态方法,但在本质上其是实例方法。这是由于.NET3.5 的运行环境仍然为 CLR2.0
所以语言不可能做很大的变革,这一切都是语法糖。
下面仍然通过一段代码来说明扩展方法的实现:
(代码出自:李永京 http://lyj.cnblogs.com)
1 public static class Extensions2 {3
IsValidEmailAddress(this string s)4
Regex(@"^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$");6
regex.IsMatch(s);7
}8 }
return
public static bool
{5
Regex regex = new
如上代码所示,扩展方法为一静态方法,声明于一个静态类,其参数前加上一个 this 关键字,参数的类型
表示这个扩展方法要对这个类型进行扩展。如上述代码表示其要对字符串类型进行扩展。
在应用上扩展方法被作为其扩展的类型的静态方法来调用。如下:
1 if (email.IsValidEmailAddress())2 {3
正确的邮件地址");4 }
Response.Write("YJingLee 提示:这是一个
Lambda 表达式
Lambda 表达式是对.NET2.0 中匿名方法在语法形式上的进一步改进,仍然以代码说明:
1 var inString = list.FindAll(delegate(string s) { return
s.Indexof("YJingLee") >= 0; });
使用 Lambda 表达式代码将更自然易懂。
1 var inString = list.FindAll(s => s.Indexof("YJingLee") >= 0);
可以看出,Lambda 表达式格式为:(参数列表)=>表达式或语句块
.NET 中的数据访问
这一部分介绍.NET 中不同的数据访问层的使用方式,由此得出 Entity Framework 在一个.NET 系统中的
应用及其在原有设计基础上的改变。从大的方面来看数据访问的设计方案基本有如下几类:
DataSet
手写代码通过 ADO.NET2.0 连接类与数据库交互
ORM 组件
DataSet 方案
最基本的 Dataset 数据访问的实现使用下图表示:
如图所示,DataSet 与数据源之间通过 DataAdapter 连接,逻辑中直接访问 DataSet 获取数据,或是通
过 ADO.NET2.0 的非连接类,或者通过强类型 DataSet 以一种类型安全的方式访问数据。
图 1
缺点逻辑代码与数据访问代码耦合高。
改进的的 DataSet 方案
图 2
这种设计方式将业务所需的实体抽象出来,并把对 DataSet 的操作封装在其中,这样一定程序上解除业务
逻辑与数据访问间的耦合。
手写代码通过 ADO.NET2.0 连接类与数据库交互
这种方式是我使用的最多的一种方式,其可以提供最大的控制能力,且效率最高,唯一的不足是当业务变
化时修改数据访问代码的工作量比较大,通过代码生成器也能一定程度上解决这个问题
ORM – LINQ to SQL
在.NET 平台下 ORM 的解决方案有不少,本文只讨论两个微软官方的解决方案。先是 LINQ to SQL 技术。
LINQ to SQL 是一个将不再更新的技术。其有很多不足之处,如,不能灵活的定义对象模型与数据表之间
的映射、无法扩展提供程序只能支持 SQL Server 等。
这样数据访问层的设计如下所示:
图 3
ORM – ADO.NET Entity Framework
作为下一代数据访问的技术领导者。Entity Framework 的设计很多地方都保留了高扩展性。其最重要的一
个改进在于其映射定义的灵活性。先来看下图:
由图可以看出,使用 Entity Framework 可以充分的定义与数据库表映射的实体,并将这个实体直接用于
业务逻辑层或作为服务的数据契约。实体设计较其他技术的优势体现在以下几方面:
图 4
创建 ComplexType(CSDL 部分有讨论)
EntitySet 的继承
使用 Entity Framework 后,可以将实体类的设计工作完全放在 EDM 的设计过程中,而不再需要手工写一
些大同小异的代码,并且对这个实体模型(包含于 EDM 中)可以在运行时修改并生效。另外,开发人员
与数据库直接打交道的次数将大大减少,大部分时间开发人员只需操作实体模型,框架会自动完成对数据
库的操作。下文将详细讨论上图所示的 EDM。
深入了解 Entity Framework
Entity Framework 的核心 – EDM(Entity Data
Model)
EDM 概述
实体数据模型,简称 EDM,由三个概念组成。概念模型由概念架构定义语言文件 (.csdl)来定义,映射由
映射规范语言文件 (.msl),存储模型(又称逻辑模型)由存储架构定义语言文件 (.ssdl)来定义。这三者合
在一起就是 EDM 模式。EDM 模式在项目中的表现形式就是扩展名为.edmx 的文件。这个包含 EDM 的文
件可以使用 Visual Studio 中的 EDM 设计器来设计。由于这个文件本质是一个 xml 文件,可以手工编辑
此文件来自定义 CSDL、MSL 与 SSDL 这三部分。下面详细分析一下这个 xml 文件及三个其重要组成部分:
这个设计器生成的文件的注释可以使你很清楚的明白这个 EDM 文件的组成。一点点分析一下,第一行表
明这是一个 xml 文件。
1
以下这一行是 EDM 的根节点,定义了一个表明版本的属性及这个 EDM 使用的命名空间:
1
接下来由注释可以看到 EDM 被分为两部分,第一部分是 EDM 的核心,第二部分用于实体设计器,这一部
分不用研究。
第一部分中节点 下定义了以下三部分:
EDM 之 CSDL
CSDL 定义了 EDM 或者说是整个程序的灵魂部分 – 概念模型。当前流行的软件设计方法通常都是由设计
其概念模型起步。说概念模型可能比较抽象一个更容易接受的名字就是实体类。实体类是面向对象设计中
一个最根本的组成部分,其体现了现实世界中对象作为一种计算中可以表示的对象设计方法。而 EDM 的
CSDL 就是要达到这样一个目的。这个在下文介绍 Entity Framework 优点时另有说明。
这个文件完全以程序语言的角度来定义模型的概念。即其中定义的实体、主键、属性、关联等都是对应
于.NET Framework 中的类型。下面 xml element 来自作业提交系统(有删节):
10
2 3
Namespace="ASSModel" Alias="Self"
xmlns="http://schemas.microsoft.com/ado/2006/04/edm"> 4
Name="ASSEntities"> 5
EntitySet="UpAssignments" ReturnType="Collection(Self.UpAssignments)"> 6
7
8
Type="String" Mode="In" /> 9
个存储过程 -->11 12
EntityType="ASSModel.Assignments" />13
EntityType="ASSModel.Classes" />14
EntityType="ASSModel.Courses" />15
EntityType="ASSModel.SetCourses" />16
EntityType="ASSModel.Students" />17
EntityType="ASSModel.Teachers" />18
EntityType="ASSModel.UpAssignments" />19
Name="FK_SetCourses_Classes" Association="ASSModel.FK_SetCourses_Classes">21
22
EntitySet="SetCourses" />23
系集 -->25
示例 -->29
Name="StuID" />32
Nullable="false" />34
Nullable="false" MaxLength="10" Unicode="true" FixedLength="true" />35
36
Name="Classes" Relationship="ASSModel.FK_Students_Classes" FromRole="Students"
ToRole="Classes" />37
Relationship="ASSModel.FK_UpAssignments_Students" FromRole="Students"
ToRole="UpAssignments" />38
41
Name="FK_SetCourses_Classes">42
Type="ASSModel.Classes" Multiplicity="1" />43
Type="ASSModel.SetCourses" Multiplicity="*" />44
46