本文档主要介绍.NET开发中两项新技术,.NET平台语言中的语言集成查询技术 - LINQ,与ADO.NET中新增的数据访问层设计技术ADO.NET Entity Framework。ADO.NET的LINQ to Entity部分以LINQ为基础,为了完整性首先介绍LINQ技术。sql
预备知识
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。 express
LINQ技术的基础 - C#3.0
-
自动属性
-
隐式类型
-
对象集合初始化器
-
匿名类
-
扩展方法
-
Lambda表达式
自动属性
这个概念很简单,其简化了咱们在.NET的时候手写一堆私有成员+属性的编程方式,咱们只须要使用以下方式声明一个属性,编译器会自动生成所需的成员变量。 编程
1 public class Customer 2 { 3 public int Id { get; set; } 4 public string Name { get; set; } 5 }
在我使用LINQ完成的项目中,使我了解到自动属性方便的一个用途以下: windows
在使用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中咱们有一种更好的方式来进行对象的初始化。那就是使用对象初始化器。这个特性也是匿名类的一个基础,因此放在匿名类以前介绍。 app
仍是那就话,好的代码强于注释,下面用几个代码段说明初始化器:
(代码出自:李永京的博客 http://lyj.cnblogs.com)
基本用法:
1 User user = new User { Id = 1, Name = "YJingLee", Age = 22 };
嵌套使用:
1 User user = new User 2 { 3 Id = 1, 4 Name = "YJingLee", 5 Age = 22, 6 Address = new Address 7 { 8 City = "NanJing", 9 Zip = 21000 10 } 11 };
相似于对象初始化器初始化一个对象,集合初始化器初始化一个集合,一句话,有了它你就不用在将元素经过Add逐个添加了。仍然给出代码示例:
基本使用:
1 List<int> num = new List<int> { 0, 1, 2, 6, 7, 8, 9 };
结合对象初始化器,咱们能够写出以下简洁的代码:
1 List<User> user = new List<User>{ 2 new User{Id=1,Name="YJingLee",Age=22}, 3 new User{Id=2,Name="XieQing",Age=25}, 4 };
应用场合:
仍是前文提到的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 Extensions 2 { 3 public static bool IsValidEmailAddress(this string s) 4 { 5 Regex regex = new Regex(@"^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$"); 6 return regex.IsMatch(s); 7 } 8 }
如上代码所示,扩展方法为一静态方法,声明于一个静态类,其参数前加上一个this关键字,参数的类型表示这个扩展方法要对这个类型进行扩展。如上述代码表示其要对字符串类型进行扩展。
在应用上扩展方法被做为其扩展的类型的静态方法来调用。以下:
1 if (email.IsValidEmailAddress()) 2 { 3 Response.Write("YJingLee提示:这是一个正确的邮件地址"); 4 }
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数据访问的实现使用下图表示:
图1
如图所示,DataSet与数据源之间经过DataAdapter链接,逻辑中直接访问DataSet获取数据,或是经过ADO.NET2.0的非链接类,或者经过强类型DataSet以一种类型安全的方式访问数据。
缺点逻辑代码与数据访问代码耦合高。
改进的的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的设计不少地方都保留了高扩展性。其最重要的一个改进在于其映射定义的灵活性。先来看下图:
图4
由图能够看出,使用Entity Framework能够充分的定义与数据库表映射的实体,并将这个实体直接用于业务逻辑层或做为服务的数据契约。实体设计较其余技术的优点体如今如下几方面:
-
建立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 <?xmlversion="1.0"encoding="utf-8"?>
如下这一行是EDM的根节点,定义了一个代表版本的属性及这个EDM使用的命名空间:
1 <edmx:EdmxVersion="1.0"xmlns:edmx="http://schemas.microsoft.com/ado/2007/06/edmx">
接下来由注释能够看到EDM被分为两部分,第一部分是EDM的核心,第二部分用于实体设计器,这一部分不用研究。
第一部分中节点 <edmx:Runtime> 下定义了如下三部分:
EDM之CSDL
CSDL定义了EDM或者说是整个程序的灵魂部分 – 概念模型。当前流行的软件设计方法一般都是由设计其概念模型起步。说概念模型可能比较抽象一个更容易接受的名字就是实体类。实体类是面向对象设计中一个最根本的组成部分,其体现了现实世界中对象做为一种计算中能够表示的对象设计方法。而EDM的CSDL就是要达到这样一个目的。这个在下文介绍Entity Framework优势时另有说明。
这个文件彻底以程序语言的角度来定义模型的概念。即其中定义的实体、主键、属性、关联等都是对应于.NET Framework中的类型。下面xml element来自做业提交系统(有删节):
1 <!-- CSDL content --> 2 <edmx:ConceptualModels> 3 <Schema Namespace="ASSModel" Alias="Self" xmlns="http://schemas.microsoft.com/ado/2006/04/edm"> 4 <EntityContainer Name="ASSEntities"> 5 <FunctionImport Name="GETHOUSEWORKDONE" EntitySet="UpAssignments" ReturnType="Collection(Self.UpAssignments)"> 6 <Parameter Name="StuID" Type="Int32" Mode="In" /> 7 <Parameter Name="ClassID" Type="Int32" Mode="In" /> 8 <Parameter Name="Semester" Type="String" Mode="In" /> 9 </FunctionImport> 10 <!-- 以上删节 – 5个存储过程 --> 11 12 <EntitySet Name="Assignments" EntityType="ASSModel.Assignments" /> 13 <EntitySet Name="Classes" EntityType="ASSModel.Classes" /> 14 <EntitySet Name="Courses" EntityType="ASSModel.Courses" /> 15 <EntitySet Name="SetCourses" EntityType="ASSModel.SetCourses" /> 16 <EntitySet Name="Students" EntityType="ASSModel.Students" /> 17 <EntitySet Name="Teachers" EntityType="ASSModel.Teachers" /> 18 <EntitySet Name="UpAssignments" EntityType="ASSModel.UpAssignments" /> 19 20 <AssociationSet Name="FK_SetCourses_Classes" Association="ASSModel.FK_SetCourses_Classes"> 21 <End Role="Classes" EntitySet="Classes" /> 22 <End Role="SetCourses" EntitySet="SetCourses" /> 23 </AssociationSet> 24 <!-- 以上删节 – 6个关系集 --> 25 26 </EntityContainer> 27 28 <!-- 如下保留一个EntityType做为示例 --> 29 <EntityType Name="Students"> 30 <Key> 31 <PropertyRef Name="StuID" /> 32 </Key> 33 <Property Name="StuID" Type="Int32" Nullable="false" /> 34 <Property Name="StuName" Type="String" Nullable="false" MaxLength="10" Unicode="true" FixedLength="true" /> 35 <Property Name="Pswd" Type="String" Nullable="false" MaxLength="50" Unicode="false" FixedLength="true" /> 36 <NavigationProperty Name="Classes" Relationship="ASSModel.FK_Students_Classes" FromRole="Students" ToRole="Classes" /> 37 <NavigationProperty Name="UpAssignments" Relationship="ASSModel.FK_UpAssignments_Students" FromRole="Students" ToRole="UpAssignments" /> 38 </EntityType> 39 40 <!-- 仅保留与上文AssociationSet对应的Association --> 41 <Association Name="FK_SetCourses_Classes"> 42 <End Role="Classes" Type="ASSModel.Classes" Multiplicity="1" /> 43 <End Role="SetCourses" Type="ASSModel.SetCourses" Multiplicity="*" /> 44 </Association> 45 </Schema> 46 </edmx:ConceptualModels>
这部分XML文档,Schema是CSDL的根元素,其中定义的Namespace是用于ObjectContext与EntityClass的命名空间,Alias-别名为此命名空间Namespace指定一个易记的名称,在定义Alias以后,在此Schema内的Element都可以该Alias做为Namespace的别名。Alias的使用能够参考以下xml element:
1 <FunctionImportName="GETHOUSEWORKDONE"EntitySet="UpAssignments"ReturnType="Collection(Self.UpAssignments)">
在这个根元素的内部的文档结构第一部分 – 实体容器大体以下:
1 <EntityContainer /> 2 <FunctionImport /> 3 <EntitySet /> 4 <AssociationSet /> 5 </EntityContainer>
下面的表格说明了这些节点及其属性的做用
EntityContainer |
|||
Name |
EntityContainer的名称,其将做为产生的ObjectContext类的名称 |
||
EntitySet |
|||
Name |
ObjectContext内与此Entity类型对应的属性名 |
||
EntityType |
ObjectContext内与此Entity类型对应的属性的类型 |
||
AssociationSet |
|||
End |
有两个End子节点,分别描述创建此关系的两个EntitySet |
||
Role |
对应到Association中End节的Role属性,起到将AssociationSet与Association相关连的做用。 |
||
FunctionImport |
详见存储过程设计部分 |
能够看出,Entity与Assciation都被分开定义与两个部分,这样设计是出于当有多个EntityContainer时,其中的EntitySet或AssociationSet能够共享Entity或Association的定义。
接下来看一下CSDL中最后一部分,Entity与Association的定义。
首先是Entity:
1 <EntityType Name="Students"> 2 <Key> 3 <PropertyRef Name="StuID" /> 4 </Key> 5 <Property Name="StuID" Type="Int32" Nullable="false" /> 6 <Property Name="StuName" Type="String" Nullable="false" MaxLength="10" Unicode="true" FixedLength="true" /> 7 <Property Name="Pswd" Type="String" Nullable="false" MaxLength="50" Unicode="false" FixedLength="true" /> 8 <NavigationProperty Name="Classes" Relationship="ASSModel.FK_Students_Classes" FromRole="Students" ToRole="Classes" /> 9 <NavigationProperty Name="UpAssignments" Relationship="ASSModel.FK_UpAssignments_Students" FromRole="Students" ToRole="UpAssignments" /> 10 </EntityType>
下表说明了其属性及其子节点与子节点的属性的含义:
EntityType |
|||
Name |
Entity Class的名称 |
||
Abstract |
是否为抽象类 |
||
BaseType |
父类 |
||
Key |
主键 |
||
Property |
主键之属性 |
||
Name |
属性名 |
||
Property |
属性 |
||
Name |
属性名 |
||
Type |
属性类型 |
||
Nullable |
是否容许null |
||
MaxLength |
属性最大长度 |
||
FixLength |
是否固定长度 |
||
NavigationProperty |
关系属性 |
||
Name |
属性名 |
||
Relationship |
对应的Association |
||
FromRole、ToRole |
区别关系两方的父与子 |
最后Association节,这是真正定义关系的地方。首先看示例:
1 <!-- 仅保留与上文AssociationSet对应的Association --> 2 <Association Name="FK_SetCourses_Classes"> 3 <End Role="Classes" Type="ASSModel.Classes" Multiplicity="1" /> 4 <End Role="SetCourses" Type="ASSModel.SetCourses" Multiplicity="*" /> 5 </Association>
这一节符合如下结构:
1 <Association> 2 <End /> 3 <ReferentialConstraint> 4 <Principal> 5 <PropertyRef /> 6 </Principal> 7 <Dependent> 8 <PropertyRef /> 9 </Dependent> 10 </ReferentialConstraint> 11 </Association>
属性及其子元素属性的说明:
Association |
||||
Name |
Association的名称 |
|||
End |
相似于AssociationSet,Association也有两个End节点。 |
|||
Name |
End名称 |
|||
Type |
EntityType的名称 |
|||
Role |
此End的Role,与AssociationSet的End的Role属性相联系 |
|||
Multiplicity |
关联多重性,值为0、1或* |
|||
ReferentialConstraint |
外键条件限制 |
|||
Principal |
主要条件 |
|||
Role |
对应于End中的Role |
|||
PropertyRef |
外键属性 |
|||
Name |
属性名称 |
|||
Dependent |
依存条件 |
|||
Role |
对应于End中的Role |
|||
PropertyRef |
外键属性 |
|||
Name |
属性名 |
另外上面示例未涉及的概念,以下:
视图
在EDM设计器中添加视图基本与添加实体表一致,所生成的xml自行对照。某些环境下可能没法添加视图,缘由未知,另外对于没有主键的表目前版本EntityFramework支持很差,在设计器中没法添加,及时经过手工编辑xml的方式强行添加,在使用过程当中也会出现问题。
ComplexType(复杂类型)
按MSDN中的例子,先描述以下场景。在一个销售系统中咱们须要在一个订单中包含一个描述客户地址的实体,而这个实体又能良好的与存储模型映射起来,因为数据库不支持地址这种类型,因此咱们能够将地址的每一个字段与数据库相映射。且在概念模型中,及在C#代码能够控制的范围内,地址仍然做为一个独立的类型存在。因为EDM设计器不支持以可视化方式建立Complex Type,咱们须要手动编辑CSDL与MSL来完成复杂类型的建立与映射。这部分示例将在介绍MSL后给出。
EDM之SSDL
这个文件中描述了表、列、关系、主键及索引等数据库中存在的概念。
1 <!-- SSDL content --> 2 <edmx:StorageModels> 3 <Schema Namespace="ASSModel.Store" Alias="Self" Provider="System.Data.SqlClient" ProviderManifestToken="2008" xmlns:store=" http://schemas.microsoft.com/ado/2007/12/edm/EntityStoreSchemaGenerator" xmlns="http://schemas.microsoft.com/ado/2006/04/edm/ssdl"> 4 <EntityContainer Name="ASSModelStoreContainer"> 5 <EntitySet Name="Assignments" EntityType="ASSModel.Store.Assignments" store:Type="Tables" Schema="dbo" /> 6 <!-- 省略7个EntitySet的定义 --> 7 </EntityContainer> 8 9 <!-- 如下省略7个EntityType的定义 --> 10 <EntityType Name="Assignments"> 11 <Key> 12 <PropertyRef Name="AssID" /> 13 </Key> 14 <Property Name="AssID" Type="int" Nullable="false" StoreGeneratedPattern="Identity" /> 15 <Property Name="AssName" Type="nchar" Nullable="false" MaxLength="20" /> 16 <Property Name="AssDes" Type="nvarchar" MaxLength="500" /> 17 <Property Name="SCID" Type="int" Nullable="false" /> 18 <Property Name="Deadline" Type="datetime" Nullable="false" /> 19 <Property Name="QuesFileName" Type="nvarchar" MaxLength="500" /> 20 <Property Name="QuesFileUrl" Type="nvarchar" Nullable="false" MaxLength="500" /> 21 </EntityType> 22 23 <!-- 保留与CSDL中对应的Function --> 24 <Function Name="GETHOUSEWORKDONE" Aggregate="false" BuiltIn="false" NiladicFunction="false" IsComposable="false" ParameterTypeSemantics="AllowImplicitConversion" Schema="dbo"> 25 <Parameter Name="StuID" Type="int" Mode="In" /> 26 <Parameter Name="ClassID" Type="int" Mode="In" /> 27 <Parameter Name="Semester" Type="varchar" Mode="In" /> 28 </Function> 29 </Schema> 30 </edmx:StorageModels>
看文档的结构,SSDL与CSDL很详细,只是其中EntityType等使用数据库的概念的描述。
这其中有一个须要稍微介绍节点,DefiningQuery,首先看一下其出现的位置:
EntityContainer |
||||
EntitySet |
||||
DefiningQuery |
经过查询定义一个SSDL的EntitySet |
|||
特定于存储的查询语句 |
DefiningQuery定义经过实体数据模型 (EDM) 内的客户端投影映射到数据存储视图的查询。此类映射是只读的。也就是说若是想要更新此类EntitySet,须要使用下文介绍存储过程时提到的定义更新实体的存储过程的方法,使用定义的存储过程来更新这样的EntitySet。当在实体类设计器中导入无主键的表时,会自动生成此类使用DefiningQuery定义的EntitySet,要式样Entity Framework提供的自动更新服务而不定义存储过程,须要给数据表添加一个适当的主键,删除DefiningQuery节点并更新数据模型。
EDM之MSL
这个文件即上面所述的CSDL与SSDL的对应,主要包括CSDL中属性与SSDL中列的对应。
1 <!-- C-S mapping content --> 2 <edmx:Mappings> 3 <Mapping Space="C-S" xmlns="urn:schemas-microsoft-com:windows:storage:mapping:CS"> 4 <EntityContainerMapping StorageEntityContainer="ASSModelStoreContainer" CdmEntityContainer="ASSEntities"> 5 <EntitySetMapping Name="Assignments"> 6 <EntityTypeMapping TypeName="IsTypeOf(ASSModel.Assignments)"> 7 <MappingFragment StoreEntitySet="Assignments"> 8 <ScalarProperty Name="QuesFileName" ColumnName="QuesFileName" /> 9 <ScalarProperty Name="AssDes" ColumnName="AssDes" /> 10 <ScalarProperty Name="AssID" ColumnName="AssID" /> 11 <ScalarProperty Name="AssName" ColumnName="AssName" /> 12 <ScalarProperty Name="Deadline" ColumnName="Deadline" /> 13 <ScalarProperty Name="QuesFileUrl" ColumnName="QuesFileUrl" /> 14 </MappingFragment> 15 </EntityTypeMapping> 16 </EntitySetMapping> 17 <!-- 省略EntitySetMapping若干 --> 18 19 <!-- 保留对应于CSDL与SSDL的FunctionImportMapping --> 20 <FunctionImportMapping FunctionImportName="GETHOUSEWORKDONE" FunctionName="ASSModel.Store.GETHOUSEWORKDONE" /> 21 22 <AssociationSetMapping Name="FK_UpAssignments_Assignments" TypeName="ASSModel.FK_UpAssignments_Assignments" StoreEntitySet="UpAssignments"> 23 <EndProperty Name="Assignments"> 24 <ScalarProperty Name="AssID" ColumnName="AssID" /> 25 </EndProperty> 26 <EndProperty Name="UpAssignments"> 27 <ScalarProperty Name="UpAssID" ColumnName="UpAssID" /> 28 </EndProperty> 29 </AssociationSetMapping> 30 <!-- 省略AssociationSetMapping若干 --> 31 </EntityContainerMapping> 32 </Mapping> 33 </edmx:Mappings>
如上代码所示,MSL的根节点为Mapping,其中能够包含多个EntityContainerMapping(上例只有一个),每个EntityContainerMapping对应着两个分别来自CSDL与SSDL的EntityContainer。这个EntityContainerMapping就是描述这两个EntityContainer间的对应。下面再给出一段代码展现EntityContainerMapping的基本格式。
1 <EntityContainerMapping StorageEntityContainer="" CdmEntityContainer=""> 2 <EntitySetMapping> 3 <EntityTypeMapping> 4 <MappingFragment> 5 <ScalarProperty /> 6 </MappingFragment> 7 <ModificationFunctionMapping> 8 <InsertFunction /> 9 <DeleteFunction /> 10 <UpdateFunction /> 11 </ ModificationFunctionMapping> 12 </EntityTypeMapping> 13 </EntitySetMapping> 14 15 <AssociationSetMapping> 16 <EndProperty> 17 <ScalarProperty /> 18 </EndProperty> 19 </AssociationSetMapping> 20 21 <FunctionImportMapping /> 22 </EntityContainerMapping>
同上文,下面列出这些节点的属性
EntityContainerMapping |
||||||
StorageEntityContainer |
SSDL中的EntityContainer名称 |
|||||
CdmEntityContainer |
CSDL中的EntityContainer名称 |
|||||
EntitySetMapping |
EntityContainer中每一个EntitySet的对应 |
|||||
Name |
EntitySetMapping的名称 |
|||||
EntityTypeMapping |
描述CSDL中EntityType与SSDL中EntityType的对应 |
|||||
Name |
EntityTypeMapping的名称 |
|||||
TypeName |
对应CSDL内Entity的名称 – 格式:IsTypeOf(<名称>) 注:这个类及其子类将共享此EntityTypeMapping |
|||||
MappingFragment |
描述属性及字段间的对应 |
|||||
StoreEntitySet |
SSDL中的EntitySet名称 (因为CSDL中一个EntitySet能够对应多个SSDL中的EntitySet) |
|||||
ScalarProperty |
属性与字段对应 |
|||||
Name |
CSDL中的属性名 |
|||||
ColumnName |
SSDL中的字段名称 |
|||||
Condition |
详见说明2 |
|||||
ColumnName |
列名 |
|||||
Value |
值 |
|||||
ModificationFunctionMapping |
CUD对应的存储过程 |
|||||
InsertFunction/ UpdateFunction / DeleteFunction |
||||||
FunctionName |
||||||
QueryView |
||||||
Entity SQL |
||||||
AssociationSetMapping |
描述CSDL中的AssociationSet与SSDL中的EntitySet的对应关系 |
|||||
Name |
AssociationSetMapping的名称 |
|||||
StoreEntitySet |
SSDL中EntitySet的名称 |
|||||
TypeName |
CSDL中AssociationSet的名称 |
|||||
EndProperty |
一个AssociationSetMapping中有两个EndProperty 分别对应CSDL中两个End Role |
|||||
Name |
EndProperty的名称 |
|||||
ScalarProperty |
关系属性对应 |
|||||
Name |
CSDL中的属性名 |
|||||
ColumnName |
SSDL中的字段名称 |
|||||
ModificationFunctionMapping |
C/D对应的存储过程 |
|||||
InsertFunction/ DeleteFunction |
||||||
FunctionName |
||||||
QueryView |
||||||
EntitySQL |
||||||
FunctionImportMapping |
用于描述CSDL与SSDL间函数及函数参数的对应(详见下文存储过程部分) |
说明1:以上表中很重要的一个属性是MappingFragment中的StoreEntitySet属性,就像这个属性的说明中所说,其描述了CSDL的Entity对应到的SSDL的Entity的名称。这是实现下文EDM映射方案中第二条将一个概念模型的实体映射到多个存储模型的实体的关键设置。
说明2:Contain这个元素及其属性的做用是,当多个概念模型实体映射到一个存储模型实体时,该元素的属性决定了在什么状况下一个概念模型实体映射到指定的存储模型实体。
说明3:QueryView 元素定义概念模型中的实体与存储模型中的实体之间的只读映射。使用根据存储模型计算的 Entity SQL 查询定义此查询视图映射,并以概念模型中的实体表达结果集。同DefiningQuery定义的查询。此映射也是只读的。就是说若是想要更新此类EntitySet,也须要使用下文介绍存储过程时提到的定义更新实体的存储过程的方法,使用定义的存储过程来更新这样的EntitySet。当多对多关联在存储模型中所映射到的实体表示关系架构中的连接表时,必须为此连接表在AssociationSetMapping 元素中定义一个QueryView元素。定义查询视图时,不能在 AssociactionSetMapping 元素上指定 StorageSetName 属性。定义查询视图时,AssociationSetMapping 元素不能同时包含 EndProperty 映射。
EDM中存储过程的设计
目前版本(VS2008SP1)的实体设计器对存储过程支持不完善,只能手工编辑这三个文件中的存储过程部分,包括:
-
CSDL中的FunctionImport元素,其属性及说明以下所示:
FunctionImport
Name
(在程序中调用的)函数的名称
EntitySet
当函数返回实体对象时,需使用此属性指定对应的EntitySet
ReturnType
函数返回值的类型
Parameter
如下用于参数子节点中的属性定义
Name
参数的名称
Type
参数的类型
Mode
参数的传递方式(In, Out或InOut)
MaxLength
参数值最大长度
Precision
参数值的精确度,用于数字类型
Scale
浮点数小数位
-
SSDL中的Function节
Function
Name
存储过程名称
Aggregate
是否为聚合函数(存储过程)
BuiltIn
是否为内建存储过程
NiladicFunction
是否为无参数存储过程
IsComposable
True为自定义函数,False为存储过程
ParameterTypeSemantics
参数类型转换方式
ReturnType
存储过程返回类型
-
MSL中的FunctionImportMapping节
FunctionImportMapping
FunctionImportName
CSDL中FunctionImport的名称
FunctionName
SSDL中Function Element的名称
这面总结的是用于返回实体对象的查询(存储过程)。
下面分别描述一下有关修改操做的存储过程的使用:
-
使用插入、更新或删除实体数据的存储过程,须要修改以下两个文件:
SSDL,对其的修改要求与上文表中列出的一致:MSL,须要对一下节点进行定义:
EntityContainerMapping |
|||||
EntitySetMapping |
EntityContainer中每一个EntitySet的对应 |
||||
EntityTypeMapping |
描述CSDL中EntityType与SSDL中EntityType的对应 |
||||
ModificationFunctionMapping |
CUD对应的存储过程 |
||||
InsertFunction/ UpdateFunction / DeleteFunction |
|||||
FunctionName |
-
使用建立或删除在数据源中使用连接表实现的实体类型之间的多对多关系的存储过程须要修改以下两个文件:
SSDL,对其的修改要求与上文表中列出的一致:MSL,须要对一下节点进行定义:
EntityContainerMapping |
||||
AssociationSetMapping |
描述CSDL中的AssociationSet与SSDL中的EntitySet的对应关系 |
|||
ModificationFunctionMapping |
C/D对应的存储过程 |
|||
InsertFunction/ DeleteFunction |
||||
FunctionName |
EDM中ComplexType的设计
再谈Complex Type,上文大体介绍了复杂类型的概念及做用,如今开始看一下具体怎样实现。前文已经提到实现复杂类型关键是在CSDL与MSL,而与SSDL无关。
首先应该在CSDL中怎加这样一节,此节与 <EntityType></EntityType> 节同级,其结构以下:
1 <ComplexType> 2 <Property /> 3 </ComplexType>
节点及其属性含义以下:
ComplexType |
复杂类型 |
|
Name |
复杂类型的名称 |
|
Property |
属性 |
|
Name |
属性名 |
|
Type |
属性类型 |
|
Nullable |
是否容许null |
|
MaxLength |
属性最大长度 |
|
FixLength |
是否固定长度 |
而后在MSL中 <MappingFragment>下与<ScalarProperty /> 同级的位置添加以下节:
1 <ComplexProperty> 2 <ScalarProperty /> 3 </ComplexProperty>
具体的节及其属性含义以下:
ComplexProperty |
复杂类型属性 |
|
Name |
复杂类型属性名称 |
|
TypeName |
CSDL中定义的ComplexType的名称。格式"CSDN_Namespace.ComplexTypeName" |
|
ScalarProperty |
关系属性对应 |
|
Name |
CSDL中的属性名 |
|
ColumnName |
SSDL中的字段名称 |
实体数据模型映射方案
实体框架支持各类方式用于在实体数据模型 (EDM) 中将概念模型映射到关系数据。有关更多信息,请参见 实体框架中的数据建模。
实体框架当前支持如下实体数据模型 (EDM) 映射方案。
编号 |
映射方案 |
说明 |
1 |
简单映射 |
在此映射方案中,概念模型中的每一个实体都映射到存储模型中的单个表。这是实体数据模型工具所生成的默认映射。 |
2 |
实体拆分 |
在此映射方案中,概念模型中单个实体的属性映射到两个或更多基础表中的列。在此方案中,表必须共享公共主键。 其设计方式见EDM之MSL部分说明1。 |
3 |
存储模型中的水平分区 |
在此映射方案中,概念模型中的单个实体类型映射到具备相同架构的两个或更多表。实体基于概念模型中定义的条件映射到表中。 使用场合:使一个概念模型的实体映射到不一样数据源的存储模型的实体。 另见:EDM之MSL部分说明2。 |
4 |
概念模型中的水平分区 |
在此映射方案中,概念模型中具备相同属性的多个实体类型映射到同一个表。条件子句用于指定表中的数据分别属于哪一个实体类型。此映射相似于类型5。 这种方式也用到MSL中的Conditon来决定映射关系,见EDM之MSL部分说明2。 |
5 |
每一个层次结构一个表继承 |
在此映射方案中,继承层次结构中的全部类型都映射到同一个表。条件子句用于定义实体类型。 见EDM之MSL部分说明2。 |
6 |
每种类型一个表继承 |
在此映射方案中,全部类型都分别映射到各自的表。仅属于某个基类型或派生类型的属性存储在映射到该类型的一个表中。 |
7 |
每种具体类型一个表继承 |
在此映射方案中,每一个非抽象类型分别映射到不一样的表。全部这些表所包含的列必须映射到派生类型的全部属性(包括从基类型继承的属性)。 |
8 |
每种类型多个实体集 |
在此映射方案中,单个实体类型在概念模型中以两个或更多独立的实体集进行表示。每一个实体集分别映射到存储模型中的一个单独的表。 其设计方式见EDM之MSL部分说明1。 |
9 |
复杂类型 |
复杂类型是没有键属性的实体类型的非标量属性。复杂类型能够包含其余嵌套的复杂类型。复杂类型映射到存储模型中的表。 复杂类型在上文有单独介绍 |
10 |
函数导入映射 |
在此方案中,存储模型中的存储过程映射到概念模型中的 FunctionImport 元素。执行此函数可以使用映射的存储过程返回实体数据。 见上文存储过程部分 |
11 |
修改函数映射 |
在此方案中,在存储模型中定义用于插入、更新和删除数据的存储过程。这些函数是为实体类型定义的,以便为特定实体类型提供更新功能。 见上文存储过程部分 |
12 |
定义查询映射 |
在此方案中,在存储模型中定义表示数据源中的表的查询。在映射到 SQL Server 数据库时,查询以数据源的本机查询语言(如 Transact-SQL)表示。此 DefiningQuery 元素映射到概念模型中的实体类型。查询以特定于存储的查询语言进行定义。 上文EDM之SSDL部分最后详细介绍了这种设计的相关问题 |
13 |
查询视图映射 |
在此方案中,会在概念模型中的实体类型与存储模型中的关系表之间定义只读映射。此映射基于对存储模型进行的 Entity SQL 查询定义。 上文EDM之MSL中说明三对这种设计的相关问题有介绍。 |
14 |
AssociationSet 映射 |
关联定义实体之间的关系。在具备一对一或一对多关联的简单映射中,在概念模型中定义关系的关联会映射到存储模型中的关联。还支持如下更高级的关联集映射: 多对多关联。关联的两端都映射到存储模型中的连接表。 自关联。此映射支持具备相同类型的两个实体之间的关联,如一个 Employee 与另外一个 Employee 之间的关联。 派生类型之间的关联。此映射支持一个层次结构中的派生类型与另外一个层次结构中的派生类型之间的关联。 |
Entity Framework的原理及使用方式
ADO.NET Entity Framework操做数据库的过程对用户是透明的(固然咱们能够经过一些工具或方法了解发送到数据库的SQL语句等)。咱们惟一能作的是操做EDM,EDM会将这个操做请求发往数据库。
Entity Framework实现了一套相似于ADO.NET2.0中链接类(它们使用方式相同,均基于Provider模式)的被称做EntityClient的类用来操做EDM。ADO.NET2.0的链接类是向数据库发送SQL命令操做表或视图,而EntityClient是向EDM发送EntitySQL操做Entity。EntityClient在EntityFramework中的做用是至关重要的,全部发往EDM的操做都是通过EntityClient,包括使用LINQ to Entity进行的操做。
各类使用方式总结
上文提到对EDM的操做,首先经过一个图来展示一下目前咱们可用的操做的EDM的方式:
这几种访问方式使用介绍以下:(部分示例代码来源MSDN Magzine)
-
EntityClient+EntitySQL
示例代码:
1 string city = "London"; 2 using (EntityConnection cn = new EntityConnection("Name=Entities")) 3 { 4 cn.Open(); 5 EntityCommand cmd = cn.CreateCommand(); 6 cmd.CommandText = @"SELECT VALUE c FROM Entities.Customers AS c WHERE 7 c.Address.City = @city"; 8 cmd.Parameters.AddWithValue("city", city); 9 DbDataReader rdr = cmd.ExecuteReader(CommandBehavior.SequentialAccess); 10 while (rdr.Read()) 11 Console.WriteLine(rdr["CompanyName"].ToString()); 12 rdr.Close(); 13 }
-
ObjectService+EntitySQL
在有EntityClient+EntitySQL这种使用方式下,使用ObjectService+EntitySQL的方式是画蛇添足,不会获得任何编辑时或运行时的好处。在ObjectContext下使用EntitySQL的真正做用是将其与LINQ to Entity结合使用。具体可见下文所示。
示例代码:
1 string city = "London"; 2 using (Entities entities = new Entities()) 3 { 4 ObjectQuery<Customers> query = entities.CreateQuery<Customers>( 5 "SELECT VALUE c FROM Customers AS c WHERE c.Address.City = @city", 6 new ObjectParameter("city", city) 7 ); 8 9 foreach (Customers c in query) 10 Console.WriteLine(c.CompanyName); 11 }
-
ObjectContext+LINQ( to Entity)
方式一:
1 string city = "London"; 2 using (Entities entities = new Entities()) 3 { 4 var query = from c in entities.Customers 5 where c.Address.City == city 6 select c; 7 8 foreach (Customers c in query) 9 Console.WriteLine(c.CompanyName); 10 }
方式二:
1 string city = "London"; 2 using (Entities entities = new Entities()) 3 { 4 var query = entities.Customers.Where(r => r.Address.City == city); 5 6 foreach (Customers c in query) 7 Console.WriteLine(c.CompanyName); 8 }
这两段示例代码中的entities.Customer的写法隐式调用了2中示例的ObjectQuery<Customers>来进行查询(关于此能够参见EDM的设计器文件-xxx.designer.cs)。在方式二中的Where方法传入的是一个Lambda表达式,你也能够传入一条EntitySQL语句作参数来将LINQ与EntitySQL结合使用。以下代码演示其使用:
1 string city = "London"; 2 using (Entities entities = new Entities()) 3 { 4 var query = entities.Customers.Where("r.Address.City = '"+city+"'"); 5 6 foreach (Customers c in query) 7 Console.WriteLine(c.CompanyName); 8 }
使用技巧及须要注意的问题
这也是上文提到的在ObjectContext下使用EntitySQL的一个主要做用,上面的例子比较简单可能看不到这样使用的优点,可是以下两种状况下使用EntitySQL多是最好的选择。
-
动态构建查询条件
当查询条件的个数固定时,咱们也能够采用罗列多个Where扩展方法的形式,以下: ObjectQuery.Where(LambdaExpression1).Where(LambdaExpression2)…
可是当这个条件的存在与否须要在运行时判断时,咱们只能经过组合字符串来获得这个条件,咱们能够将条件组合为EntitySQL并传递给Where()方法。 -
数据库模糊查询
下面代码演示使用EntitySQL的like完成模糊查询:
context.Customer.Where("it.CustomerID LIKE @CustomerID", new System.Data.Objects.ObjectParameter("CustomerID","%V%"));
这个并非只能使用EntitySQL来实现,LINQ to Entity也能够很容易完成。以下代码: context.Customer.Where(r => r.CustomerID.Contains("V"));
同理,"V%"、"%V"能够分别使用StartsWith()与EndsWith()函数实现。
使用LINQ to Entity须要注意的一个方面是,在完成查询获得须要的结果后使用ToList或ToArray方法将结果转变为内存中的对象,而后使用LINQ to Objects来处理,不然处在Entity Framework的联机模式下对性能有很大的影响。
几种方法的性能分析及使用选择
首先用下图来讲明一个执行过程。
图中所示表达的意思已经很是清楚,稍加解释的是,不管是经过EntityClient直接提供给Entity Client Data Provider的Entity SQL仍是经过ObjectService传递的Entity SQL(或是LINQ to Entity),都在Entity Client Data Provider中被解释为相应的Command Tree,并进一步解释为对应数据库的SQL。这样来看使用LINQ to Entity与Entity SQL的效率应该差很少,可是还有一个问题,那就是EntitySQL所转换的最终SQL可能要比LINQ to Entity生成的SQL效率高,这在必定程度上使二者效率差增大,可是LINQ to Entity有其它技术没法比拟的好处,那就是它的强类型特性,编辑时智能感知提醒,编译时发现错误,这都是在一个大型项目中所须要的。虽然如今也有了调试EntitySQL的工具,但其与强类型的LINQ to Entity仍是有很大差距。
另外在ObjectService与直接使用EntityClient问题的选择上。若是你想更灵活的控制查询过程,或者进行临时查询建议选择EntityCLient,若是是操做数据那只能采用ObjectService。
上文总结了各类操做EDM的方式,下面引用MSDN的一个对这几种技术进行比较的表格:
|
EntityClient 和实体 SQL |
对象服务和实体 SQL |
对象服务和 LINQ |
定向到 EntityClient 提供程序 |
是 |
否 |
否 |
适合临时查询 |
是 |
是 |
否 |
可直接发出 DML |
否 |
否 |
否 |
强类型化 |
否 |
否 |
是 |
可将实体做为结果返回 |
否 |
是 |
是 |
经过这个表能够很好对某一场合下应该选择的技术进行判断。EntityClient 和实体 SQL能够进行最大的控制,而使用LINQ to Entity能够得到最佳的编辑时支持。
其它操做EDM的方式
经过EdmGen更灵活的控制EDM
在.NET Framework 3.5的文件夹下有一个名为EdmGen的工具,Visual Studio的实体设计器就是调用这个工具来完成EDM的生成等操做。经过直接使用这个工具的命令行选项咱们能够进行更多的控制。
这个命令的参数及做用以下:
EdmGen 选项
/mode:EntityClassGeneration 从 csdl 文件生成对象
/mode:FromSsdlGeneration 从 ssdl 文件生成 msl、csdl 和对象
/mode:ValidateArtifacts 验证 ssdl、msl 和 csdl 文件
/mode:ViewGeneration 从 ssdl、msl 和 csdl 文件生成映射视图
/mode:FullGeneration 从数据库生成 ssdl、msl、csdl 和对象
/project:<字符串> 用于全部项目文件的基名称 (短格式: /p)
/provider:<字符串> 用于 ssdl 生成的 Ado.Net 数据提供程序的名称。(短格式: /prov)
/connectionstring:<链接字符串> 您要链接到的数据库的链接字符串 (短格式: /c)
/incsdl:<文件> 从中读取概念模型的文件
/refcsdl:<文件> 包含 /incsdl 文件所依赖的类型的 csdl 文件
/inmsl:<文件> 从中读取映射的文件
/inssdl:<文件> 从中读取存储模型的文件
/outcsdl:<文件> 将生成的概念模型写入到其中的文件
/outmsl:<文件> 将生成的映射写入到其中的文件
/outssdl:<文件> 将生成的存储模型写入到其中的文件
/outobjectlayer:<文件> 将生成的对象层写入到其中的文件
/outviews:<文件> 将预生成的视图对象写入到其中的文件
/language:CSharp 使用 C# 语言生成代码
/language:VB 使用 VB 语言生成代码
/namespace:<字符串> 用于概念模型类型的命名空间名称
/entitycontainer:<字符串> 用于概念模型中的 EntityContainer 的名称
/help 显示用法信息 (短格式: /?)
/nologo 取消显示版权消息
使用示例:
从 Northwind 示例数据库生成完整 Entity Model。
1 EdmGen /mode:FullGeneration /project:Northwind /provider:System.Data.SqlClient /connectionstring:"server=.\sqlexpress;integrated security=true; database=northwind"
从 ssdl 文件开始生成 Entity Model。
1 EdmGen /mode:FromSSDLGeneration /inssdl:Northwind.ssdl /project:Northwind
验证 Entity Model。
1 EdmGen /mode:ValidateArtifacts /inssdl:Northwind.ssdl /inmsl:Northwind.msl /incsdl:Northwind.csdl
为何要使用Entity Framework,限制条件及当前版本框架的问题
-
优点
经过对比上面图4与图二、图3咱们能够很清楚的看到使用Entity Framework一个很大的好处,咱们能够把实体类的定义由一个单独的项目使用C# class完成这样一种设计方式转变为使用xml文件定义并集成到数据访问层。
在以往要在一个项目中动态建立实体,我所知的方法是把要添加的实体放入一个程序集,而后经过反射加载程序集。如今能够经过动态更改EDM的方法来增长实体并将其映射到数据库,后者是之前没法实现的。
便于更改数据库,当更换数据库后,只需修改SSDL的定义,(若是数据库的代表有变更,也只需多修改MSL),对CSDL没有任何影响,从而也不须要对程序的BLL等上层部分作任何改动。
-
条件
要想让一个数据库支持Entity Framework,一个必要条件就是该数据库需提供相应的Entity Client Data Provider,这样才能将Entity SQL转换为针对此数据此数据库的SQL并交由ADO.NET来执行。固然该数据库还须要提供ADO.NET Data Provider。
-
缺陷
Entity Framework技术的效率问题是其几乎惟一一个稍有不足之处。首先其将EntitySQL转换为SQL的方式属于解释性转换,性能较差。另外Entity Framework在每次应用启动时须要读取EDM,这个过程较慢(但在后续操做时,就再也不存在这个问题)。
EDM中的DML
因为当前的EntitySQL不支持DML操做,因此当前版本的Entity Framework的插入、更新及删除操做须要经过Object Service来完成。在EDM的设计器文件xxx.designer.cs中自动生成了一些签名为
void AddToEntity(EntityType entity)
的方法。咱们只须要新建一个实体对象并调用这个方法添加实体便可。注意这个函数内部调用
entities.AddObject("EntitySetName", entity);
最后调用entities.SaveChanges()方法将修改保存回数据库,这是全部三种更新操做所需的。更新与删除操做都须要先使用ObjectService定位操做的实体对象,更新操做直接使用赋值运算符,删除操做则调用
entites.DeleteObject(object o);
方法。以后调用entities.SaveChanges()方法保存,这个过程简单,再也不赘述。
含有Association的EDM的使用
当前版本的Entity Framework不支持自动延迟加载,全部当前未使用的关系中的相关实体默认按不加载处理,当咱们须要经过关系获取一个实体对象时,咱们能够采用两种方法:
-
显示加载
实体框架针对 EntityReference 类的每一个实例提供一个 Load 方法。此方法可用于显式加载与另外一实体相关的一个集合。咱们只需在访问关系中实体以前调用其Load便可,固然提早判断该实体是否已经加载是一种比较好的实践。以下代码所示:1 using (Entities entities = new Entities()) 2 { 3 var query = (from o in entities.Orders 4 where o.Customers.CustomerID == "ALFKI" 5 select o); 6 foreach (Orders order in query) 7 { 8 if (!order.CustomersReference.IsLoaded) 9 order.CustomersReference.Load(); 10 Console.WriteLine(order.OrderID + " --- " + 11 order.Customers.CompanyName); 12 } 13 }
-
预先加载
先看代码示例
1 using (Entities entities = new Entities()) 2 { 3 var query = (from o in entities.Orders.Include("Customers") 4 where o.ShipCountry == "USA" 5 select o); 6 7 foreach (Orders order in query) 8 Console.WriteLine(order.OrderID + " --- " + 9 order.Customers.CompanyName); 10 }
查询中针对 Orders 实体调用的 Include 方法接受了一个参数,该参数在本示例中将要求查询不只要检索 Orders,并且还要检索相关的 Customers。这将生成单个 SQL 语句,它会加载知足 LINQ 查询条件的全部 Order 和 Customer。
两种加载关系实体的方式的选择根据:若是针对关系数据你只需作一到两次查询,则使用显示加载更高效,若是要持续访问关系实体中数据,则使用预先加载。
关系下的添加,更新与删除与上述操做基本相同,惟一须要注意的是删除操做不支持级联删除,须要手工遍历全部的相关项并将其一一删除。注意这里删除操做不能使用foreach来遍历须要删除的关系实体。取而代之的有两种方法:
-
while法
1 while (result.Order_Details.Count > 0) 2 { 3 // 删除操做… 4 }
-
ToList法(以非联机方式操做)
1 var items = result.Order_Details.ToList(); 2 foreach (var item in items) 3 { 4 // 删除操做… 5 }
最新补充
Entity Framework在开发中的应用 – Entity Framework与控件
.NET Framework提供了许多xxxDataSource控件,如SqlDataSource,ObjectDataSource等,这些数据源控件大大方便了咱们的数据绑定操做。不幸的是目前尚未针对Entity Framework的数据源控件发布,可是将数据绑定到诸如ListBox,Grrdview或DetailsView控件也是很简单的。这源于使用ObjectContext操做返回的IQueryable<T>对象或是使用EntityClient查询返回的ObjectQuery对象都实现了IEnumerable接口。这样很容易将这些数据绑定到数据显示控件。更新操做能够按上文所述在相应的时间处理函数中写更新EDM的程序便可。
Entity Framework的连接字符串
默认状况下(Visual Studio对Entity Framework数据项目的默认设置),EDM这个XML文件被做为资源在编译时嵌入到程序集中。这种状况下当更改EDM后须要从新编译这个程序集才能使更改生效。经过更改项目属性也可让EDM做为三个独立的XML文件存在于项目中。为了让应用程序能够找到EDM(不管其以什么方式存储)须要一个连接字符串来指示EDM所在的位置。实体模型设计器生成的连接字符串以下所示:
1 <add name="ASSEntities" 2 connectionString=" 3 metadata=res://*/ass.csdl| 4 res://*/ass.ssdl| 5 res://*/ass.msl; 6 provider=System.Data.SqlClient; 7 provider connection string="Data Source=(local);Initial Catalog=ASS;Integrated Security=True;MultipleActiveResultSets=True"" 8 providerName="System.Data.EntityClient" />
http://msdn.microsoft.com/zh-cn/library/cc716756.aspx
关键的一点应用程序是怎样找到这个字符串的,对于使用EntityClient的状况,能够直接将链接字符串赋给EntityConnection的ConnectionString属性,另外对于使用ObjectContext,其能够自动由配置文件检索这个链接字符串。