[(转)hystar整理]Entity Framework 教程
預(yù)備知識????2
LINQ技術(shù)????2
LINQ技術(shù)的基礎(chǔ) - C#3.0????2
自動(dòng)屬性????2
隱式類型????2
對象初始化器與集合初始化器????3
匿名類????3
擴(kuò)展方法????4
Lambda表達(dá)式????4
.NET中的數(shù)據(jù)訪問????4
DataSet方案????5
改進(jìn)的的DataSet方案????5
手寫代碼通過ADO.NET2.0連接類與數(shù)據(jù)庫交互????5
ORM – LINQ to SQL????6
深入了解Entity Framework????7
Entity Framework的核心 – EDM(Entity Data Model)????7
EDM概述????7
EDM之CSDL????7
EDM之SSDL????11
EDM之MSL????12
EDM中存儲(chǔ)過程的設(shè)計(jì)????15
EDM中ComplexType的設(shè)計(jì)????16
實(shí)體數(shù)據(jù)模型映射方案????17
Entity Framework的原理及使用方式????18
各種使用方式總結(jié)????18
使用技巧及需要注意的問題????21
幾種方法的性能分析及使用選擇????21
其它操作EDM的方式????22
為什么要使用Entity Framework,限制條件及當(dāng)前版本框架的問題????23
EDM中的DML????23
含有Association的EDM的使用????23
?
?
本文檔主要介紹.NET開發(fā)中兩項(xiàng)新技術(shù),.NET平臺(tái)語言中的語言集成查詢技術(shù) - LINQ,與ADO.NET中新增的數(shù)據(jù)訪問層設(shè)計(jì)技術(shù)ADO.NET Entity Framework。ADO.NET的LINQ to Entity部分以LINQ為基礎(chǔ),為了完整性本文檔首先介紹LINQ技術(shù)。
預(yù)備知識
LINQ技術(shù)
LINQ是.NET 3.5中新增的一種技術(shù),這個(gè)技術(shù)擴(kuò)展了.NET平臺(tái)上的編程語言,使其可以更加方便的進(jìn)行數(shù)據(jù)查詢,單純的LINQ技術(shù)主要完成對集合對象(如System.Collection下或System.Collection.Generic命名空間下的對象)的查詢。結(jié)合LINQ Provider可以實(shí)現(xiàn)對XML文件(使用LINQ to XML – 位于System.Xml.Linq命名空間下的類),數(shù)據(jù)庫(可以使用LINQ to SQL或下文要詳細(xì)介紹的LINQ to Entity)等對象的操作。
?
LINQ是一種運(yùn)行時(shí)無關(guān)的技術(shù),其運(yùn)行于CLR2.0之上,微軟對C#3.0與VB9.0的編譯器進(jìn)性擴(kuò)展,從而使其可以將LINQ編寫的程序編譯為可以被CLR2.0的JIT所理解的MSIL。
?
LINQ技術(shù)的基礎(chǔ) - C#3.0
自動(dòng)屬性
這個(gè)概念很簡單,其簡化了我們在.NET的時(shí)候手寫一堆私有成員+屬性的編程方式,我們只需要使用如下方式聲明一個(gè)屬性,編譯器會(huì)自動(dòng)生成所需的成員變量。
public class Customer
{
public int Id { get; set; }
public string Name { get; set; }
}
????在我使用LINQ完成的項(xiàng)目中,使我了解到自動(dòng)屬性方便的一個(gè)用途如下:
????在使用LINQ獲取數(shù)據(jù)的過程中,我們常常需要使用select new語句查詢出一個(gè)對象(往往是IEnumerable類型的)用于數(shù)據(jù)綁定。在一般情況下如果是直接綁定(如直接將查詢結(jié)果賦給一個(gè)Gridview控件的DataSource屬性)我們可以直接select new來返回一個(gè)匿名類的對象。如果我們還需要對這個(gè)集合對象進(jìn)行進(jìn)一步操作,我們將必須使用select new class-name這樣的語言返回一個(gè)類的對象,大部分情況下這個(gè)類只作為實(shí)體的一個(gè)結(jié)構(gòu)而不需要完成一些操作操作,這時(shí)候使用自動(dòng)屬性來完成這個(gè)類將是非常簡潔高效的。
隱式類型
這個(gè)名稱可能對你很陌生,但是var這個(gè)關(guān)鍵字應(yīng)該都用過,在C#中使用var聲明一個(gè)對象時(shí),編譯器會(huì)自動(dòng)根據(jù)其賦值語句推斷這個(gè)局部變量的類型。賦值以后,這個(gè)變量的類型也就確定而不可以再進(jìn)行更改。另外var關(guān)鍵字也用于匿名類的聲明。
????應(yīng)用場合:var主要用途是表示一個(gè)LINQ查詢的結(jié)果。這個(gè)結(jié)果可能是ObjectQuery<>或IQueryable<>類型的對象,也可能是一個(gè)簡單的實(shí)體類型的對象。這時(shí)使用var聲明這個(gè)對象可以節(jié)省很多代碼書寫上的時(shí)間。
對象初始化器與集合初始化器
????在.NET2.0中構(gòu)造一個(gè)對象的方法一是提供一個(gè)重載的構(gòu)造函數(shù),二是用默認(rèn)的構(gòu)造函數(shù)生成一個(gè)對象,然后對其屬性進(jìn)行賦值。在.NET3.5/C#3.0中我們有一種更好的方式來進(jìn)行對象的初始化。那就是使用對象初始化器。這個(gè)特性也是匿名類的一個(gè)基礎(chǔ),所以放在匿名類之前介紹。
????還是那就話,好的代碼強(qiáng)于注釋,下面用幾個(gè)代碼段說明初始化器:
(代碼出自:李永京的博客 http://lyj.cnblogs.com/)
基本用法:
User user = new User { Id = 1, Name = "YJingLee", Age = 22 };
?
嵌套使用:
User user = new User
{
?? ??Id = 1,
?? ??Name = "YJingLee",
?? ??Age = 22,
?? ??Address = new Address
?? ??{
?? ???? ??City = "NanJing",
?? ???? ??Zip = 21000
?? ??}
};
????類似于對象初始化器初始化一個(gè)對象,集合初始化器初始化一個(gè)集合,一句話,有了它你就不用在將元素通過Add逐個(gè)添加了。仍然給出代碼示例:
基本使用:
List<int> num = new List<int> { 0, 1, 2, 6, 7, 8, 9 };
?
結(jié)合對象初始化器,我們可以寫出如下簡潔的代碼:
List<User> user = new List<User>{
new User{Id=1,Name="YJingLee",Age=22},
new User{Id=2,Name="XieQing",Age=25},
};
應(yīng)用場合:
????還是前文提到的select new class-name語法,后面可以直接接一個(gè)初始化器來將查詢結(jié)果返回到這個(gè)對象。
匿名類
有了前文初始化器的介紹,匿名類就很簡單了。我們可以使用new { object initializer }或new[]{ object, …}來初始化一個(gè)匿名類或不確定類型的數(shù)組。匿名類的對象需要使用var關(guān)鍵字聲明。示例代碼:
var p1 = new { Id = 1, Name = "YJingLee", Age = 22 };
應(yīng)用場合:
還是同上面的例子提到的當(dāng)直接使用select new { object initializer }這樣的語法就是將一個(gè)LINQ查詢的結(jié)果返回到一個(gè)匿名類中。
擴(kuò)展方法
????擴(kuò)展方法是C#中新增的很重要的特性之一。其對于LINQ的實(shí)現(xiàn)起著關(guān)鍵的作用。在.NET2.0時(shí)代是沒有LINQ的,所以.NET2.0以及之前版本中的集合類在設(shè)計(jì)的時(shí)候沒有預(yù)留用于LINQ的方法。為了在不破壞這個(gè)類現(xiàn)有封裝的前提下又可以為其添加LINQ的支持就需要用到擴(kuò)展方法。
????擴(kuò)展方法使用上類似于靜態(tài)方法,但在本質(zhì)上其是實(shí)例方法。這是由于.NET3.5的運(yùn)行環(huán)境仍然為CLR2.0所以語言不可能做很大的變革,這一切都是語法糖。
下面仍然通過一段代碼來說明擴(kuò)展方法的實(shí)現(xiàn):
(代碼出自:李永京 http://lyj.cnblogs.com/)
public static class Extensions
{
public static bool IsValidEmailAddress(this string s)
????{
Regex regex = new Regex(@"^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$");
return regex.IsMatch(s);
????}
}
如上代碼所示,擴(kuò)展方法為一靜態(tài)方法,聲明于一個(gè)靜態(tài)類,其參數(shù)前加上一個(gè)this關(guān)鍵字,參數(shù)的類型表示這個(gè)擴(kuò)展方法要對這個(gè)類型進(jìn)行擴(kuò)展。如上述代碼表示其要對字符串類型進(jìn)行擴(kuò)展。
在應(yīng)用上擴(kuò)展方法被作為其擴(kuò)展的類型的靜態(tài)方法來調(diào)用。如下:
if (email.IsValidEmailAddress())
{
????Response.Write("YJingLee提示:這是一個(gè)正確的郵件地址");
}
Lambda表達(dá)式
Lambda表達(dá)式是對.NET2.0中匿名方法在語法形式上的進(jìn)一步改進(jìn),仍然以代碼說明:
var inString = list.FindAll(delegate(string s) { return s.Indexof("YJingLee") >= 0; });
?
使用Lambda表達(dá)式代碼將更自然易懂。
var inString = list.FindAll(s => s.Indexof("YJingLee") >= 0);
?
可以看出,Lambda表達(dá)式格式為:(參數(shù)列表)=>表達(dá)式或語句塊
另外我對于Lambda表達(dá)式樹的概念還不是很明白,有明白的指點(diǎn)一下。
?
.NET中的數(shù)據(jù)訪問
這一部分介紹.NET中不同的數(shù)據(jù)訪問層的使用方式,由此得出Entity Framework在一個(gè).NET系統(tǒng)中的應(yīng)用及其在原有設(shè)計(jì)基礎(chǔ)上的改變。從大的方面來看數(shù)據(jù)訪問的設(shè)計(jì)方案基本有如下幾類:
- DataSet
- 手寫代碼通過ADO.NET2.0連接類與數(shù)據(jù)庫交互
- ORM組件
DataSet方案
最基本的Dataset數(shù)據(jù)訪問的實(shí)現(xiàn)使用下圖表示:
圖1
如圖所示,DataSet與數(shù)據(jù)源之間通過DataAdapter連接,邏輯中直接訪問DataSet獲取數(shù)據(jù),或是通過ADO.NET2.0的非連接類,或者通過強(qiáng)類型DataSet以一種類型安全的方式訪問數(shù)據(jù)。
????缺點(diǎn)邏輯代碼與數(shù)據(jù)訪問代碼耦合高。
改進(jìn)的的DataSet方案
圖2
這種設(shè)計(jì)方式將業(yè)務(wù)所需的實(shí)體抽象出來,并把對DataSet的操作封裝在其中,這樣一定程序上解除業(yè)務(wù)邏輯與數(shù)據(jù)訪問間的耦合。
手寫代碼通過ADO.NET2.0連接類與數(shù)據(jù)庫交互
這種方式是我使用的最多的一種方式,其可以提供最大的控制能力,且效率最高,唯一的不足是當(dāng)業(yè)務(wù)變化時(shí)修改數(shù)據(jù)訪問代碼的工作量比較大,通過代碼生成器也能一定程度上解決這個(gè)問題
?
ORM – LINQ to SQL
在.NET平臺(tái)下ORM的解決方案有不少,本文只討論兩個(gè)微軟官方的解決方案。先是LINQ to SQL技術(shù)。LINQ to SQL是一個(gè)將不再更新的技術(shù)。其有很多不足之處,如,不能靈活的定義對象模型與數(shù)據(jù)表之間的映射、無法擴(kuò)展提供程序只能支持SQL Server等。
這樣數(shù)據(jù)訪問層的設(shè)計(jì)如下所示:
圖3
?
ORM – ADO.NET Entity Framework
作為下一代數(shù)據(jù)訪問的技術(shù)領(lǐng)導(dǎo)者。Entity Framework的設(shè)計(jì)很多地方都保留了高擴(kuò)展性。其最重要的一個(gè)改進(jìn)在于其映射定義的靈活性。先來看下圖:
圖4
由圖可以看出,使用Entity Framework可以充分的定義與數(shù)據(jù)庫表映射的實(shí)體,并將這個(gè)實(shí)體直接用于業(yè)務(wù)邏輯層或作為服務(wù)的數(shù)據(jù)契約。實(shí)體設(shè)計(jì)較其他技術(shù)的優(yōu)勢體現(xiàn)在以下幾方面:
- 創(chuàng)建ComplexType(CSDL部分有討論)
- EntitySet的繼承
使用Entity Framework后,可以將實(shí)體類的設(shè)計(jì)工作完全放在EDM的設(shè)計(jì)過程中,而不再需要手工寫一些大同小異的代碼,并且對這個(gè)實(shí)體模型(包含于EDM中)可以在運(yùn)行時(shí)修改并生效。另外,開發(fā)人員與數(shù)據(jù)庫直接打交道的次數(shù)將大大減少,大部分時(shí)間開發(fā)人員只需操作實(shí)體模型,框架會(huì)自動(dòng)完成對數(shù)據(jù)庫的操作。下文將詳細(xì)討論上圖所示的EDM。
深入了解Entity Framework
Entity Framework的核心 – EDM(Entity Data Model)
EDM概述
實(shí)體數(shù)據(jù)模型,簡稱EDM,由三個(gè)概念組成。概念模型由概念架構(gòu)定義語言文件 (.csdl)來定義,映射由映射規(guī)范語言文件 (.msl),存儲(chǔ)模型(又稱邏輯模型)由存儲(chǔ)架構(gòu)定義語言文件 (.ssdl)來定義。這三者合在一起就是EDM模式。EDM模式在項(xiàng)目中的表現(xiàn)形式就是擴(kuò)展名為.edmx的文件。這個(gè)包含EDM的文件可以使用Visual Studio中的EDM設(shè)計(jì)器來設(shè)計(jì)。由于這個(gè)文件本質(zhì)是一個(gè)xml文件,可以手工編輯此文件來自定義CSDL、MSL與SSDL這三部分。下面詳細(xì)分析一下這個(gè)xml文件及三個(gè)其重要組成部分:
這個(gè)文件展示了示例項(xiàng)目完整的EDM文件的XML形式:
文件
這個(gè)設(shè)計(jì)器生成的文件的注釋可以使你很清楚的明白這個(gè)EDM文件的組成。一點(diǎn)點(diǎn)分析一下,第一行表明這是一個(gè)xml文件。
| <?xml version="1.0" encoding="utf-8"?> |
以下這一行是EDM的根節(jié)點(diǎn),定義了一個(gè)表明版本的屬性及這個(gè)EDM使用的命名空間:
| <edmx:Edmx Version="1.0" xmlns:edmx="http://schemas.microsoft.com/ado/2007/06/edmx"> |
接下來由注釋可以看到EDM被分為兩部分,第一部分是EDM的核心,第二部分用于實(shí)體設(shè)計(jì)器,這一部分不用研究。
第一部分中節(jié)點(diǎn)<edmx:Runtime>下定義了以下三部分:
EDM之CSDL
????CSDL定義了EDM或者說是整個(gè)程序的靈魂部分 – 概念模型。當(dāng)前流行的軟件設(shè)計(jì)方法通常都是由設(shè)計(jì)其概念模型起步。說概念模型可能比較抽象一個(gè)更容易接受的名字就是實(shí)體類。實(shí)體類是面向?qū)ο笤O(shè)計(jì)中一個(gè)最根本的組成部分,其體現(xiàn)了現(xiàn)實(shí)世界中對象作為一種計(jì)算中可以表示的對象設(shè)計(jì)方法。而EDM的CSDL就是要達(dá)到這樣一個(gè)目的。這個(gè)在下文介紹Entity Framework優(yōu)點(diǎn)時(shí)另有說明。
????這個(gè)文件完全以程序語言的角度來定義模型的概念。即其中定義的實(shí)體、主鍵、屬性、關(guān)聯(lián)等都是對應(yīng)于.NET Framework中的類型。下面xml element來自作業(yè)提交系統(tǒng)(有刪節(jié)):
| <!-- CSDL content --> <edmx:ConceptualModels> <Schema Namespace="ASSModel" Alias="Self" xmlns="http://schemas.microsoft.com/ado/2006/04/edm"> <EntityContainer Name="ASSEntities"> <FunctionImport Name="GETHOUSEWORKDONE" EntitySet="UpAssignments" ReturnType="Collection(Self.UpAssignments)"> <Parameter Name="StuID" Type="Int32" Mode="In" /> <Parameter Name="ClassID" Type="Int32" Mode="In" /> <Parameter Name="Semester" Type="String" Mode="In" /> </FunctionImport> <!-- 以上刪節(jié) – 5個(gè)存儲(chǔ)過程 --> ? <EntitySet Name="Assignments" EntityType="ASSModel.Assignments" /> <EntitySet Name="Classes" EntityType="ASSModel.Classes" /> <EntitySet Name="Courses" EntityType="ASSModel.Courses" /> <EntitySet Name="SetCourses" EntityType="ASSModel.SetCourses" /> <EntitySet Name="Students" EntityType="ASSModel.Students" /> <EntitySet Name="Teachers" EntityType="ASSModel.Teachers" /> <EntitySet Name="UpAssignments" EntityType="ASSModel.UpAssignments" /> ? <AssociationSet Name="FK_SetCourses_Classes" Association="ASSModel.FK_SetCourses_Classes"> <End Role="Classes" EntitySet="Classes" /> <End Role="SetCourses" EntitySet="SetCourses" /> </AssociationSet> <!-- 以上刪節(jié) – 6個(gè)關(guān)系集 --> ? </EntityContainer> ? <!-- 以下保留一個(gè)EntityType作為示例 --> <EntityType Name="Students"> <Key> <PropertyRef Name="StuID" /> </Key> <Property Name="StuID" Type="Int32" Nullable="false" /> <Property Name="StuName" Type="String" Nullable="false" MaxLength="10" Unicode="true" FixedLength="true" /> <Property Name="Pswd" Type="String" Nullable="false" MaxLength="50" Unicode="false" FixedLength="true" /> <NavigationProperty Name="Classes" Relationship="ASSModel.FK_Students_Classes" FromRole="Students" ToRole="Classes" /> <NavigationProperty Name="UpAssignments" Relationship="ASSModel.FK_UpAssignments_Students" FromRole="Students" ToRole="UpAssignments" /> </EntityType> ? <!-- 僅保留與上文AssociationSet對應(yīng)的Association --> <Association Name="FK_SetCourses_Classes"> <End Role="Classes" Type="ASSModel.Classes" Multiplicity="1" /> <End Role="SetCourses" Type="ASSModel.SetCourses" Multiplicity="*" /> </Association> </Schema> </edmx:ConceptualModels> |
這部分XML文檔,Schema是CSDL的根元素,其中定義的Namespace是用于ObjectContext與EntityClass的命名空間,Alias-別名為此命名空間Namespace指定一個(gè)易記的名稱,在定義Alias之后,在此Schema內(nèi)的Element均可以該Alias作為Namespace的別名。Alias的使用可以參考如下xml element:
| <FunctionImport Name="GETHOUSEWORKDONE" EntitySet="UpAssignments" ReturnType="Collection(Self.UpAssignments)"> |
在這個(gè)根元素的內(nèi)部的文檔結(jié)構(gòu)第一部分 – 實(shí)體容器大致如下:
| <EntityContainer /> <FunctionImport /> <EntitySet /> <AssociationSet /> </EntityContainer> |
下面的表格說明了這些節(jié)點(diǎn)及其屬性的作用
| EntityContainer | ? | ||
| Name | EntityContainer的名稱,其將作為產(chǎn)生的ObjectContext類的名稱 | ||
| ? | EntitySet | ? | |
| Name | ObjectContext內(nèi)與此Entity類型對應(yīng)的屬性名 | ||
| EntityType | ObjectContext內(nèi)與此Entity類型對應(yīng)的屬性的類型 | ||
| AssociationSet | ? | ||
| ? | End | 有兩個(gè)End子節(jié)點(diǎn),分別描述建立此關(guān)系的兩個(gè)EntitySet | |
| ? | Role | 對應(yīng)到Association中End節(jié)的Role屬性,起到將AssociationSet與Association相關(guān)連的作用。 | |
| ? | FunctionImport | 詳見存儲(chǔ)過程設(shè)計(jì)部分 | |
可以看出,Entity與Assciation都被分開定義與兩個(gè)部分,這樣設(shè)計(jì)是出于當(dāng)有多個(gè)EntityContainer時(shí),其中的EntitySet或AssociationSet可以共享Entity或Association的定義。
????接下來看一下CSDL中最后一部分,Entity與Association的定義。
首先是Entity:
<EntityType Name="Students">
<Key>
<PropertyRef Name="StuID" />
</Key>
<Property Name="StuID" Type="Int32" Nullable="false" />
<Property Name="StuName" Type="String" Nullable="false" MaxLength="10" Unicode="true" FixedLength="true" />
<Property Name="Pswd" Type="String" Nullable="false" MaxLength="50" Unicode="false" FixedLength="true" />
<NavigationProperty Name="Classes" Relationship="ASSModel.FK_Students_Classes" FromRole="Students" ToRole="Classes" />
<NavigationProperty Name="UpAssignments" Relationship="ASSModel.FK_UpAssignments_Students" FromRole="Students" ToRole="UpAssignments" />
</EntityType>
下表說明了其屬性及其子節(jié)點(diǎn)與子節(jié)點(diǎn)的屬性的含義:
?
?
?
?
?
?
?
?
?
?
| EntityType | ? | ||
| Name | Entity Class的名稱 | ||
| Abstract | 是否為抽象類 | ||
| BaseType | 父類 | ||
| ? | Key | 主鍵 | |
| ? | Property | 主鍵之屬性 | |
| Name | 屬性名 | ||
| Property | 屬性 | ||
| Name | 屬性名 | ||
| Type | 屬性類型 | ||
| Nullable | 是否允許null | ||
| MaxLength | 屬性最大長度 | ||
| FixLength | 是否固定長度 | ||
| NavigationProperty | 關(guān)系屬性 | ||
| Name | 屬性名 | ||
| Relationship | 對應(yīng)的Association | ||
| FromRole、ToRole | 區(qū)別關(guān)系兩方的父與子 | ||
?
最后Association節(jié),這是真正定義關(guān)系的地方。首先看示例:
<!-- 僅保留與上文AssociationSet對應(yīng)的Association -->
<Association Name="FK_SetCourses_Classes">
<End Role="Classes" Type="ASSModel.Classes" Multiplicity="1" />
<End Role="SetCourses" Type="ASSModel.SetCourses" Multiplicity="*" />
</Association>
這一節(jié)符合以下結(jié)構(gòu):
| <Association> <End /> <ReferentialConstraint> <Principal> <PropertyRef /> </Principal> <Dependent> <PropertyRef /> </Dependent> </ReferentialConstraint> </Association> |
屬性及其子元素屬性的說明:
?
?
| Association | ? | |||
| Name | Association的名稱 | |||
| ? | End | 類似于AssociationSet,Association也有兩個(gè)End節(jié)點(diǎn)。 | ||
| Name | End名稱 | |||
| Type | EntityType的名稱 | |||
| Role | 此End的Role,與AssociationSet的End的Role屬性相聯(lián)系 | |||
| Multiplicity | 關(guān)聯(lián)多重性,值為0、1或* | |||
| ReferentialConstraint | 外鍵條件限制 | |||
| ? | Principal | 主要條件 | ||
| Role | 對應(yīng)于End中的Role | |||
| ? | PropertyRef | 外鍵屬性 | ||
| Name | 屬性名稱 | |||
| Dependent | 依存條件 | |||
| Role | 對應(yīng)于End中的Role | |||
| ? | PropertyRef | 外鍵屬性 | ||
| Name | 屬性名 | |||
?
另外上面示例未涉及的概念,如下:
視圖
????在EDM設(shè)計(jì)器中添加視圖基本與添加實(shí)體表一致,所生成的xml自行對照。某些環(huán)境下可能無法添加視圖,原因未知,另外對于沒有主鍵的表目前版本EntityFramework支持不好,在設(shè)計(jì)器中無法添加,及時(shí)通過手工編輯xml的方式強(qiáng)行添加,在使用過程中也會(huì)出現(xiàn)問題。
ComplexType(復(fù)雜類型)
按MSDN中的例子,先描述如下場景。在一個(gè)銷售系統(tǒng)中我們需要在一個(gè)訂單中包含一個(gè)描述客戶地址的實(shí)體,而這個(gè)實(shí)體又能良好的與存儲(chǔ)模型映射起來,由于數(shù)據(jù)庫不支持地址這種類型,所以我們可以將地址的每個(gè)字段與數(shù)據(jù)庫相映射。且在概念模型中,及在C#代碼可以控制的范圍內(nèi),地址仍然作為一個(gè)獨(dú)立的類型存在。由于EDM設(shè)計(jì)器不支持以可視化方式創(chuàng)建Complex Type,我們需要手動(dòng)編輯CSDL與MSL來完成復(fù)雜類型的創(chuàng)建與映射。這部分示例將在介紹MSL后給出。
?
EDM之SSDL
這個(gè)文件中描述了表、列、關(guān)系、主鍵及索引等數(shù)據(jù)庫中存在的概念。
| <!-- SSDL content --> <edmx:StorageModels> <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"> <EntityContainer Name="ASSModelStoreContainer"> <EntitySet Name="Assignments" EntityType="ASSModel.Store.Assignments" store:Type="Tables" Schema="dbo" /> <!-- 省略7個(gè)EntitySet的定義 --> </EntityContainer> ? <!-- 以下省略7個(gè)EntityType的定義 --> <EntityType Name="Assignments"> <Key> <PropertyRef Name="AssID" /> </Key> <Property Name="AssID" Type="int" Nullable="false" StoreGeneratedPattern="Identity" /> <Property Name="AssName" Type="nchar" Nullable="false" MaxLength="20" /> <Property Name="AssDes" Type="nvarchar" MaxLength="500" /> <Property Name="SCID" Type="int" Nullable="false" /> <Property Name="Deadline" Type="datetime" Nullable="false" /> <Property Name="QuesFileName" Type="nvarchar" MaxLength="500" /> <Property Name="QuesFileUrl" Type="nvarchar" Nullable="false" MaxLength="500" /> </EntityType> ? <!-- 保留與CSDL中對應(yīng)的Function --> <Function Name="GETHOUSEWORKDONE" Aggregate="false" BuiltIn="false" NiladicFunction="false" IsComposable="false" ParameterTypeSemantics="AllowImplicitConversion" Schema="dbo"> <Parameter Name="StuID" Type="int" Mode="In" /> <Parameter Name="ClassID" Type="int" Mode="In" /> <Parameter Name="Semester" Type="varchar" Mode="In" /> </Function> ? </Schema> </edmx:StorageModels> |
看文檔的結(jié)構(gòu),SSDL與CSDL很詳細(xì),只是其中EntityType等使用數(shù)據(jù)庫的概念的描述。
這其中有一個(gè)需要稍微介紹節(jié)點(diǎn),DefiningQuery,首先看一下其出現(xiàn)的位置:
| EntityContainer | ? | |||
| ? | EntitySet | ? | ||
| ? | DefiningQuery | 通過查詢定義一個(gè)SSDL的EntitySet | ||
| ? | 特定于存儲(chǔ)的查詢語句 | |||
DefiningQuery定義通過實(shí)體數(shù)據(jù)模型 (EDM) 內(nèi)的客戶端投影映射到數(shù)據(jù)存儲(chǔ)視圖的查詢。此類映射是只讀的。也就是說如果想要更新此類EntitySet,需要使用下文介紹存儲(chǔ)過程時(shí)提到的定義更新實(shí)體的存儲(chǔ)過程的方法,使用定義的存儲(chǔ)過程來更新這樣的EntitySet。當(dāng)在實(shí)體類設(shè)計(jì)器中導(dǎo)入無主鍵的表時(shí),會(huì)自動(dòng)生成此類使用DefiningQuery定義的EntitySet,要式樣Entity Framework提供的自動(dòng)更新服務(wù)而不定義存儲(chǔ)過程,需要給數(shù)據(jù)表添加一個(gè)適當(dāng)?shù)闹麈I,刪除DefiningQuery節(jié)點(diǎn)并更新數(shù)據(jù)模型。
EDM之MSL
這個(gè)文件即上面所述的CSDL與SSDL的對應(yīng),主要包括CSDL中屬性與SSDL中列的對應(yīng)。
| <!-- C-S mapping content --> <edmx:Mappings> <Mapping Space="C-S" xmlns="urn:schemas-microsoft-com:windows:storage:mapping:CS"> <EntityContainerMapping StorageEntityContainer="ASSModelStoreContainer" CdmEntityContainer="ASSEntities"> <EntitySetMapping Name="Assignments"> <EntityTypeMapping TypeName="IsTypeOf(ASSModel.Assignments)"> <MappingFragment StoreEntitySet="Assignments"> <ScalarProperty Name="QuesFileName" ColumnName="QuesFileName" /> <ScalarProperty Name="AssDes" ColumnName="AssDes" /> <ScalarProperty Name="AssID" ColumnName="AssID" /> <ScalarProperty Name="AssName" ColumnName="AssName" /> <ScalarProperty Name="Deadline" ColumnName="Deadline" /> <ScalarProperty Name="QuesFileUrl" ColumnName="QuesFileUrl" /> </MappingFragment> </EntityTypeMapping> </EntitySetMapping> <!-- 省略EntitySetMapping若干 --> ? <!-- 保留對應(yīng)于CSDL與SSDL的FunctionImportMapping --> <FunctionImportMapping FunctionImportName="GETHOUSEWORKDONE" FunctionName="ASSModel.Store.GETHOUSEWORKDONE" /> ? <AssociationSetMapping Name="FK_UpAssignments_Assignments" TypeName="ASSModel.FK_UpAssignments_Assignments" StoreEntitySet="UpAssignments"> <EndProperty Name="Assignments"> <ScalarProperty Name="AssID" ColumnName="AssID" /> </EndProperty> <EndProperty Name="UpAssignments"> <ScalarProperty Name="UpAssID" ColumnName="UpAssID" /> </EndProperty> </AssociationSetMapping> <!-- 省略AssociationSetMapping若干 --> ? </EntityContainerMapping> </Mapping> </edmx:Mappings> |
????如上代碼所示,MSL的根節(jié)點(diǎn)為Mapping,其中可以包含多個(gè)EntityContainerMapping(上例只有一個(gè)),每一個(gè)EntityContainerMapping對應(yīng)著兩個(gè)分別來自CSDL與SSDL的EntityContainer。這個(gè)EntityContainerMapping就是描述這兩個(gè)EntityContainer間的對應(yīng)。下面再給出一段代碼展示EntityContainerMapping的基本格式。
| <EntityContainerMapping StorageEntityContainer="" CdmEntityContainer=""> <EntitySetMapping> <EntityTypeMapping> <MappingFragment> <ScalarProperty /> </MappingFragment> <ModificationFunctionMapping> <InsertFunction /> <DeleteFunction /> <UpdateFunction /> </ ModificationFunctionMapping> </EntityTypeMapping> </EntitySetMapping> ? <AssociationSetMapping> <EndProperty> <ScalarProperty /> </EndProperty> </AssociationSetMapping> ? <FunctionImportMapping /> </EntityContainerMapping> |
同上文,下面列出這些節(jié)點(diǎn)的屬性
| EntityContainerMapping | ? | |||||
| StorageEntityContainer | SSDL中的EntityContainer名稱 | |||||
| CdmEntityContainer | CSDL中的EntityContainer名稱 | |||||
| ? | EntitySetMapping | EntityContainer中每個(gè)EntitySet的對應(yīng) | ||||
| Name | EntitySetMapping的名稱 | |||||
| ? | EntityTypeMapping | 描述CSDL中EntityType與SSDL中EntityType的對應(yīng) | ||||
| Name | EntityTypeMapping的名稱 | |||||
| TypeName | 對應(yīng)CSDL內(nèi)Entity的名稱 – 格式:IsTypeOf(<名稱>) 注:這個(gè)類及其子類將共享此EntityTypeMapping | |||||
| ? | MappingFragment | 描述屬性及字段間的對應(yīng) | ||||
| StoreEntitySet | SSDL中的EntitySet名稱 (由于CSDL中一個(gè)EntitySet可以對應(yīng)多個(gè)SSDL中的EntitySet) | |||||
| ? | ScalarProperty | 屬性與字段對應(yīng) | ||||
| Name | CSDL中的屬性名 | |||||
| ColumnName | SSDL中的字段名稱 | |||||
| ? | Condition | 詳見說明2 | ||||
| ColumnName | 列名 | |||||
| Value | 值 | |||||
| ModificationFunctionMapping | CUD對應(yīng)的存儲(chǔ)過程 | |||||
| ? | InsertFunction/ UpdateFunction / DeleteFunction | |||||
| FunctionName | ? | |||||
| QueryView | ? | |||||
| ? | Entity SQL | |||||
| AssociationSetMapping | 描述CSDL中的AssociationSet與SSDL中的EntitySet的對應(yīng)關(guān)系 | |||||
| Name | AssociationSetMapping的名稱 | |||||
| StoreEntitySet | SSDL中EntitySet的名稱 | |||||
| TypeName | CSDL中AssociationSet的名稱 | |||||
| ? | EndProperty | 一個(gè)AssociationSetMapping中有兩個(gè)EndProperty 分別對應(yīng)CSDL中兩個(gè)End Role | ||||
| Name | EndProperty的名稱 | |||||
| ? | ScalarProperty | 關(guān)系屬性對應(yīng) | ||||
| Name | CSDL中的屬性名 | |||||
| ColumnName | SSDL中的字段名稱 | |||||
| ModificationFunctionMapping | C/D對應(yīng)的存儲(chǔ)過程 | |||||
| ? | InsertFunction/ DeleteFunction | |||||
| FunctionName | ? | |||||
| QueryView | ? | |||||
| ? | EntitySQL | |||||
| FunctionImportMapping | 用于描述CSDL與SSDL間函數(shù)及函數(shù)參數(shù)的對應(yīng)(詳見下文存儲(chǔ)過程部分) | |||||
?
說明1:以上表中很重要的一個(gè)屬性是MappingFragment中的StoreEntitySet屬性,就像這個(gè)屬性的說明中所說,其描述了CSDL的Entity對應(yīng)到的SSDL的Entity的名稱。這是實(shí)現(xiàn)下文EDM映射方案中第二條將一個(gè)概念模型的實(shí)體映射到多個(gè)存儲(chǔ)模型的實(shí)體的關(guān)鍵設(shè)置。
說明2:Contain這個(gè)元素及其屬性的作用是,當(dāng)多個(gè)概念模型實(shí)體映射到一個(gè)存儲(chǔ)模型實(shí)體時(shí),該元素的屬性決定了在什么情況下一個(gè)概念模型實(shí)體映射到指定的存儲(chǔ)模型實(shí)體。
說明3:QueryView 元素定義概念模型中的實(shí)體與存儲(chǔ)模型中的實(shí)體之間的只讀映射。使用根據(jù)存儲(chǔ)模型計(jì)算的 Entity SQL 查詢定義此查詢視圖映射,并以概念模型中的實(shí)體表達(dá)結(jié)果集。同DefiningQuery定義的查詢。此映射也是只讀的。就是說如果想要更新此類EntitySet,也需要使用下文介紹存儲(chǔ)過程時(shí)提到的定義更新實(shí)體的存儲(chǔ)過程的方法,使用定義的存儲(chǔ)過程來更新這樣的EntitySet。當(dāng)多對多關(guān)聯(lián)在存儲(chǔ)模型中所映射到的實(shí)體表示關(guān)系架構(gòu)中的鏈接表時(shí),必須為此鏈接表在AssociationSetMapping 元素中定義一個(gè)QueryView元素。定義查詢視圖時(shí),不能在 AssociactionSetMapping 元素上指定 StorageSetName 屬性。定義查詢視圖時(shí),AssociationSetMapping 元素不能同時(shí)包含 EndProperty 映射。
EDM中存儲(chǔ)過程的設(shè)計(jì)
目前版本(VS2008SP1)的實(shí)體設(shè)計(jì)器對存儲(chǔ)過程支持不完善,只能手工編輯這三個(gè)文件中的存儲(chǔ)過程部分,包括:
| FunctionImport | ? | |
| Name | (在程序中調(diào)用的)函數(shù)的名稱 | |
| EntitySet | 當(dāng)函數(shù)返回實(shí)體對象時(shí),需使用此屬性指定對應(yīng)的EntitySet | |
| ReturnType | 函數(shù)返回值的類型 | |
| ? | Parameter | 以下用于參數(shù)子節(jié)點(diǎn)中的屬性定義 |
| Name | 參數(shù)的名稱 | |
| Type | 參數(shù)的類型 | |
| Mode | 參數(shù)的傳遞方式(In, Out或InOut) | |
| MaxLength | 參數(shù)值最大長度 | |
| Precision | 參數(shù)值的精確度,用于數(shù)字類型 | |
| Scale | 浮點(diǎn)數(shù)小數(shù)位 | |
| Function | ? |
| Name | 存儲(chǔ)過程名稱 |
| Aggregate | 是否為聚合函數(shù)(存儲(chǔ)過程) |
| BuiltIn | 是否為內(nèi)建存儲(chǔ)過程 |
| NiladicFunction | 是否為無參數(shù)存儲(chǔ)過程 |
| IsComposable | True為自定義函數(shù),False為存儲(chǔ)過程 |
| ParameterTypeSemantics | 參數(shù)類型轉(zhuǎn)換方式 |
| ReturnType | 存儲(chǔ)過程返回類型 |
| FunctionImportMapping | ? |
| FunctionImportName | CSDL中FunctionImport的名稱 |
| FunctionName | SSDL中Function Element的名稱 |
這面總結(jié)的是用于返回實(shí)體對象的查詢(存儲(chǔ)過程)。
下面分別描述一下有關(guān)修改操作的存儲(chǔ)過程的使用:
SSDL,對其的修改要求與上文表中列出的一致:
MSL,需要對一下節(jié)點(diǎn)進(jìn)行定義:
| EntityContainerMapping | ? | ||||
| ? | EntitySetMapping | EntityContainer中每個(gè)EntitySet的對應(yīng) | |||
| ? | EntityTypeMapping | 描述CSDL中EntityType與SSDL中EntityType的對應(yīng) | |||
| ? | ModificationFunctionMapping | CUD對應(yīng)的存儲(chǔ)過程 | |||
| ? | InsertFunction/ UpdateFunction / DeleteFunction | ||||
| FunctionName | ? | ||||
?
SSDL,對其的修改要求與上文表中列出的一致:
MSL,需要對一下節(jié)點(diǎn)進(jìn)行定義:
| EntityContainerMapping | ? | |||
| ? | AssociationSetMapping | 描述CSDL中的AssociationSet與SSDL中的EntitySet的對應(yīng)關(guān)系 | ||
| ? | ModificationFunctionMapping | C/D對應(yīng)的存儲(chǔ)過程 | ||
| ? | InsertFunction/ DeleteFunction | |||
| FunctionName | ? | |||
?
EDM中ComplexType的設(shè)計(jì)
再談Complex Type,上文大致介紹了復(fù)雜類型的概念及作用,現(xiàn)在開始看一下具體怎樣實(shí)現(xiàn)。前文已經(jīng)提到實(shí)現(xiàn)復(fù)雜類型關(guān)鍵是在CSDL與MSL,而與SSDL無關(guān)。
????首先應(yīng)該在CSDL中怎加這樣一節(jié),此節(jié)與<EntityType></EntityType>節(jié)同級,其結(jié)構(gòu)如下:
| <ComplexType> <Property /> </ComplexType> |
節(jié)點(diǎn)及其屬性含義如下:
| ComplexType | 復(fù)雜類型 | |
| Name | 復(fù)雜類型的名稱 | |
| ? | Property | 屬性 |
| Name | 屬性名 | |
| Type | 屬性類型 | |
| Nullable | 是否允許null | |
| MaxLength | 屬性最大長度 | |
| FixLength | 是否固定長度 | |
????然后在MSL中<MappingFragment>下與<ScalarProperty />同級的位置添加如下節(jié):
| <ComplexProperty> <ScalarProperty /> </ComplexProperty> |
具體的節(jié)及其屬性含義如下:
| ComplexProperty | 復(fù)雜類型屬性 | |
| Name | 復(fù)雜類型屬性名稱 | |
| TypeName | CSDL中定義的ComplexType的名稱。格式"CSDN_Namespace.ComplexTypeName" | |
| ? | ScalarProperty | 關(guān)系屬性對應(yīng) |
| Name | CSDL中的屬性名 | |
| ColumnName | SSDL中的字段名稱 | |
?
實(shí)體數(shù)據(jù)模型映射方案
實(shí)體框架支持各種方式用于在實(shí)體數(shù)據(jù)模型 (EDM) 中將概念模型映射到關(guān)系數(shù)據(jù)。有關(guān)更多信息,請參見 實(shí)體框架中的數(shù)據(jù)建模。
實(shí)體框架當(dāng)前支持以下實(shí)體數(shù)據(jù)模型 (EDM) 映射方案。
| 編號 | 映射方案 | 說明 |
| 1 | 簡單映射 | 在此映射方案中,概念模型中的每個(gè)實(shí)體都映射到存儲(chǔ)模型中的單個(gè)表。這是實(shí)體數(shù)據(jù)模型工具所生成的默認(rèn)映射。 |
| 2 | 實(shí)體拆分 | 在此映射方案中,概念模型中單個(gè)實(shí)體的屬性映射到兩個(gè)或更多基礎(chǔ)表中的列。在此方案中,表必須共享公共主鍵。 其設(shè)計(jì)方式見EDM之MSL部分說明1。 |
| 3 | 存儲(chǔ)模型中的水平分區(qū) | 在此映射方案中,概念模型中的單個(gè)實(shí)體類型映射到具有相同架構(gòu)的兩個(gè)或更多表。實(shí)體基于概念模型中定義的條件映射到表中。 使用場合:使一個(gè)概念模型的實(shí)體映射到不同數(shù)據(jù)源的存儲(chǔ)模型的實(shí)體。 另見:EDM之MSL部分說明2。 |
| 4 | 概念模型中的水平分區(qū) | 在此映射方案中,概念模型中具有相同屬性的多個(gè)實(shí)體類型映射到同一個(gè)表。條件子句用于指定表中的數(shù)據(jù)分別屬于哪個(gè)實(shí)體類型。此映射類似于類型5。 這種方式也用到MSL中的Conditon來決定映射關(guān)系,見EDM之MSL部分說明2。 |
| 5 | 每個(gè)層次結(jié)構(gòu)一個(gè)表繼承 | 在此映射方案中,繼承層次結(jié)構(gòu)中的所有類型都映射到同一個(gè)表。條件子句用于定義實(shí)體類型。 見EDM之MSL部分說明2。 |
| 6 | 每種類型一個(gè)表繼承 | 在此映射方案中,所有類型都分別映射到各自的表。僅屬于某個(gè)基類型或派生類型的屬性存儲(chǔ)在映射到該類型的一個(gè)表中。 |
| 7 | 每種具體類型一個(gè)表繼承 | 在此映射方案中,每個(gè)非抽象類型分別映射到不同的表。所有這些表所包含的列必須映射到派生類型的所有屬性(包括從基類型繼承的屬性)。 |
| 8 | 每種類型多個(gè)實(shí)體集 | 在此映射方案中,單個(gè)實(shí)體類型在概念模型中以兩個(gè)或更多獨(dú)立的實(shí)體集進(jìn)行表示。每個(gè)實(shí)體集分別映射到存儲(chǔ)模型中的一個(gè)單獨(dú)的表。 其設(shè)計(jì)方式見EDM之MSL部分說明1。 |
| 9 | 復(fù)雜類型 | 復(fù)雜類型是沒有鍵屬性的實(shí)體類型的非標(biāo)量屬性。復(fù)雜類型可以包含其他嵌套的復(fù)雜類型。復(fù)雜類型映射到存儲(chǔ)模型中的表。 復(fù)雜類型在上文有單獨(dú)介紹 |
| 10 | 函數(shù)導(dǎo)入映射 | 在此方案中,存儲(chǔ)模型中的存儲(chǔ)過程映射到概念模型中的 FunctionImport 元素。執(zhí)行此函數(shù)可使用映射的存儲(chǔ)過程返回實(shí)體數(shù)據(jù)。 見上文存儲(chǔ)過程部分 |
| 11 | 修改函數(shù)映射 | 在此方案中,在存儲(chǔ)模型中定義用于插入、更新和刪除數(shù)據(jù)的存儲(chǔ)過程。這些函數(shù)是為實(shí)體類型定義的,以便為特定實(shí)體類型提供更新功能。 見上文存儲(chǔ)過程部分 |
| 12 | 定義查詢映射 | 在此方案中,在存儲(chǔ)模型中定義表示數(shù)據(jù)源中的表的查詢。在映射到 SQL Server 數(shù)據(jù)庫時(shí),查詢以數(shù)據(jù)源的本機(jī)查詢語言(如 Transact-SQL)表示。此 DefiningQuery 元素映射到概念模型中的實(shí)體類型。查詢以特定于存儲(chǔ)的查詢語言進(jìn)行定義。 上文EDM之SSDL部分最后詳細(xì)介紹了這種設(shè)計(jì)的相關(guān)問題 |
| 13 | 查詢視圖映射 | 在此方案中,會(huì)在概念模型中的實(shí)體類型與存儲(chǔ)模型中的關(guān)系表之間定義只讀映射。此映射基于對存儲(chǔ)模型進(jìn)行的 Entity SQL 查詢定義。 上文EDM之MSL中說明三對這種設(shè)計(jì)的相關(guān)問題有介紹。 |
| 14 | AssociationSet 映射 | 關(guān)聯(lián)定義實(shí)體之間的關(guān)系。在具有一對一或一對多關(guān)聯(lián)的簡單映射中,在概念模型中定義關(guān)系的關(guān)聯(lián)會(huì)映射到存儲(chǔ)模型中的關(guān)聯(lián)。還支持以下更高級的關(guān)聯(lián)集映射: 多對多關(guān)聯(lián)。關(guān)聯(lián)的兩端都映射到存儲(chǔ)模型中的鏈接表。 自關(guān)聯(lián)。此映射支持具有相同類型的兩個(gè)實(shí)體之間的關(guān)聯(lián),如一個(gè) Employee 與另一個(gè) Employee 之間的關(guān)聯(lián)。 派生類型之間的關(guān)聯(lián)。此映射支持一個(gè)層次結(jié)構(gòu)中的派生類型與另一個(gè)層次結(jié)構(gòu)中的派生類型之間的關(guān)聯(lián)。 |
?
Entity Framework的原理及使用方式
ADO.NET Entity Framework操作數(shù)據(jù)庫的過程對用戶是透明的(當(dāng)然我們可以通過一些工具或方法了解發(fā)送到數(shù)據(jù)庫的SQL語句等)。我們唯一能做的是操作EDM,EDM會(huì)將這個(gè)操作請求發(fā)往數(shù)據(jù)庫。
????Entity Framework實(shí)現(xiàn)了一套類似于ADO.NET2.0中連接類(它們使用方式相同,均基于Provider模式)的被稱作EntityClient的類用來操作EDM。ADO.NET2.0的連接類是向數(shù)據(jù)庫發(fā)送SQL命令操作表或視圖,而EntityClient是向EDM發(fā)送EntitySQL操作Entity。EntityClient在EntityFramework中的作用是相當(dāng)重要的,所有發(fā)往EDM的操作都是經(jīng)過EntityClient,包括使用LINQ to Entity進(jìn)行的操作。
各種使用方式總結(jié)
????上文提到對EDM的操作,首先通過一個(gè)圖來展現(xiàn)一下目前我們可用的操作的EDM的方式:
?
這幾種訪問方式使用介紹如下:(部分示例代碼來源MSDN Magzine)
示例代碼:
string city = "London";
using (EntityConnection cn = new EntityConnection("Name=Entities"))
{
cn.Open();
EntityCommand cmd = cn.CreateCommand();
cmd.CommandText = @"SELECT VALUE c FROM Entities.Customers AS c WHERE
c.Address.City = @city";
cmd.Parameters.AddWithValue("city", city);
DbDataReader rdr = cmd.ExecuteReader(CommandBehavior.SequentialAccess);
while (rdr.Read())
Console.WriteLine(rdr["CompanyName"].ToString());
rdr.Close();
}
?
在有EntityClient+EntitySQL這種使用方式下,使用ObjectService+EntitySQL的方式是多此一舉,不會(huì)得到任何編輯時(shí)或運(yùn)行時(shí)的好處。在ObjectContext下使用EntitySQL的真正作用是將其與LINQ to Entity結(jié)合使用。具體可見下文所示。
示例代碼:
string city = "London";
using (Entities entities = new Entities())
{
ObjectQuery<Customers> query = entities.CreateQuery<Customers>(
"SELECT VALUE c FROM Customers AS c WHERE c.Address.City = @city",
new ObjectParameter("city", city)
);
?
foreach (Customers c in query)
Console.WriteLine(c.CompanyName);
}
?
方式一:
string city = "London";
using (Entities entities = new Entities())
{
var query = from c in entities.Customers
where c.Address.City == city
select c;
?
foreach (Customers c in query)
Console.WriteLine(c.CompanyName);
}
????方式二:
string city = "London";
using (Entities entities = new Entities())
{
var query = entities.Customers.Where(r => r.Address.City == city);
?
foreach (Customers c in query)
Console.WriteLine(c.CompanyName);
}
這兩段示例代碼中的entities.Customer的寫法隱式調(diào)用了2中示例的ObjectQuery<Customers>來進(jìn)行查詢(關(guān)于此可以參見EDM的設(shè)計(jì)器文件-xxx.designer.cs)。在方式二中的Where方法傳入的是一個(gè)Lambda表達(dá)式,你也可以傳入一條EntitySQL語句做參數(shù)來將LINQ與EntitySQL結(jié)合使用。如下代碼演示其使用:
string city = "London";
using (Entities entities = new Entities())
{
var query = entities.Customers.Where("r.Address.City = '"+city+"'");
?
foreach (Customers c in query)
Console.WriteLine(c.CompanyName);
}
使用技巧及需要注意的問題
這也是上文提到的在ObjectContext下使用EntitySQL的一個(gè)主要作用,上面的例子比較簡單可能看不到這樣使用的優(yōu)勢,但是如下兩種情況下使用EntitySQL可能是最好的選擇。
當(dāng)查詢條件的個(gè)數(shù)固定時(shí),我們也可以采用羅列多個(gè)Where擴(kuò)展方法的形式,如下:
| ObjectQuery.Where(LambdaExpression1) .Where(LambdaExpression2)… |
但是當(dāng)這個(gè)條件的存在與否需要在運(yùn)行時(shí)判斷時(shí),我們只能通過組合字符串來得到這個(gè)條件,我們可以將條件組合為EntitySQL并傳遞給Where()方法。
下面代碼演示使用EntitySQL的like完成模糊查詢:
| context.Customer.Where("it.CustomerID LIKE @CustomerID", new System.Data.Objects.ObjectParameter("CustomerID","%V%")); |
這個(gè)并不是只能使用EntitySQL來實(shí)現(xiàn),LINQ to Entity也可以很容易完成。如下代碼:
| context.Customer.Where(r => r.CustomerID.Contains("V")); |
同理,"V%"、"%V"可以分別使用StartsWith()與EndsWith()函數(shù)實(shí)現(xiàn)。
?
????使用LINQ to Entity需要注意的一個(gè)方面是,在完成查詢得到需要的結(jié)果后使用ToList或ToArray方法將結(jié)果轉(zhuǎn)變?yōu)閮?nèi)存中的對象,然后使用LINQ to Objects來處理,否則處在Entity Framework的聯(lián)機(jī)模式下對性能有很大的影響。
?
幾種方法的性能分析及使用選擇
首先用下圖來說明一個(gè)執(zhí)行過程。
????圖中所示表達(dá)的意思已經(jīng)非常清楚,稍加解釋的是,無論是通過EntityClient直接提供給Entity Client Data Provider的Entity SQL還是通過ObjectService傳遞的Entity SQL(或是LINQ to Entity),都在Entity Client Data Provider中被解釋為相應(yīng)的Command Tree,并進(jìn)一步解釋為對應(yīng)數(shù)據(jù)庫的SQL。這樣來看使用LINQ to Entity與Entity SQL的效率應(yīng)該差不多,但是還有一個(gè)問題,那就是EntitySQL所轉(zhuǎn)換的最終SQL可能要比LINQ to Entity生成的SQL效率高,這在一定程度上使兩者效率差增大,但是LINQ to Entity有其它技術(shù)無法比擬的好處,那就是它的強(qiáng)類型特性,編輯時(shí)智能感知提醒,編譯時(shí)發(fā)現(xiàn)錯(cuò)誤,這都是在一個(gè)大型項(xiàng)目中所需要的。雖然現(xiàn)在也有了調(diào)試EntitySQL的工具,但其與強(qiáng)類型的LINQ to Entity還是有很大差距。
????另外在ObjectService與直接使用EntityClient問題的選擇上。如果你想更靈活的控制查詢過程,或者進(jìn)行臨時(shí)查詢建議選擇EntityCLient,如果是操作數(shù)據(jù)那只能采用ObjectService。
?
上文總結(jié)了各種操作EDM的方式,下面引用MSDN的一個(gè)對這幾種技術(shù)進(jìn)行比較的表格:
| ?? | EntityClient 和實(shí)體 SQL | 對象服務(wù)和實(shí)體 SQL | 對象服務(wù)和 LINQ |
| 定向到 EntityClient 提供程序 | 是 | 否 | 否 |
| 適合臨時(shí)查詢 | 是 | 是 | 否 |
| 可直接發(fā)出 DML | 否 | 否 | 否 |
| 強(qiáng)類型化 | 否 | 否 | 是 |
| 可將實(shí)體作為結(jié)果返回 | 否 | 是 | 是 |
通過這個(gè)表可以很好對某一場合下應(yīng)該選擇的技術(shù)進(jìn)行判斷。EntityClient 和實(shí)體 SQL可以進(jìn)行最大的控制,而使用LINQ to Entity可以獲得最佳的編輯時(shí)支持。
?
其它操作EDM的方式
通過EdmGen更靈活的控制EDM
在.NET Framework 3.5的文件夾下有一個(gè)名為EdmGen的工具,Visual Studio的實(shí)體設(shè)計(jì)器就是調(diào)用這個(gè)工具來完成EDM的生成等操作。通過直接使用這個(gè)工具的命令行選項(xiàng)我們可以進(jìn)行更多的控制。
這個(gè)命令的參數(shù)及作用如下:
| EdmGen 選項(xiàng) /mode:EntityClassGeneration 從 csdl 文件生成對象 /mode:FromSsdlGeneration 從 ssdl 文件生成 msl、csdl 和對象 /mode:ValidateArtifacts 驗(yàn)證 ssdl、msl 和 csdl 文件 /mode:ViewGeneration 從 ssdl、msl 和 csdl 文件生成映射視圖 /mode:FullGeneration 從數(shù)據(jù)庫生成 ssdl、msl、csdl 和對象 /project:<字符串> 用于所有項(xiàng)目文件的基名稱 (短格式: /p) /provider:<字符串> 用于 ssdl 生成的 Ado.Net 數(shù)據(jù)提供程序的名稱。(短格式: /prov) /connectionstring:<連接字符串> 您要連接到的數(shù)據(jù)庫的連接字符串 (短格式: /c) /incsdl:<文件> 從中讀取概念模型的文件 /refcsdl:<文件> 包含 /incsdl 文件所依賴的類型的 csdl 文件 /inmsl:<文件> 從中讀取映射的文件 /inssdl:<文件> 從中讀取存儲(chǔ)模型的文件 /outcsdl:<文件> 將生成的概念模型寫入到其中的文件 /outmsl:<文件> 將生成的映射寫入到其中的文件 /outssdl:<文件> 將生成的存儲(chǔ)模型寫入到其中的文件 /outobjectlayer:<文件> 將生成的對象層寫入到其中的文件 /outviews:<文件> 將預(yù)生成的視圖對象寫入到其中的文件 /language:CSharp 使用 C# 語言生成代碼 /language:VB 使用 VB 語言生成代碼 /namespace:<字符串> 用于概念模型類型的命名空間名稱 /entitycontainer:<字符串> 用于概念模型中的 EntityContainer 的名稱 /help 顯示用法信息 (短格式: /?) /nologo 取消顯示版權(quán)消息 |
?
使用示例:
從 Northwind 示例數(shù)據(jù)庫生成完整 Entity Model。
| EdmGen /mode:FullGeneration /project:Northwind /provider:System.Data.SqlClient /connectionstring:"server=.\sqlexpress;integrated security=true; database=northwind" |
從 ssdl 文件開始生成 Entity Model。
| EdmGen /mode:FromSSDLGeneration /inssdl:Northwind.ssdl /project:Northwind |
驗(yàn)證 Entity Model。
| EdmGen /mode:ValidateArtifacts /inssdl:Northwind.ssdl /inmsl:Northwind.msl /incsdl:Northwind.csdl |
?
為什么要使用Entity Framework,限制條件及當(dāng)前版本框架的問題
- 優(yōu)勢
通過對比上面圖4與圖2、圖3我們可以很清楚的看到使用Entity Framework一個(gè)很大的好處,我們可以把實(shí)體類的定義由一個(gè)單獨(dú)的項(xiàng)目使用C# class完成這樣一種設(shè)計(jì)方式轉(zhuǎn)變?yōu)槭褂脁ml文件定義并集成到數(shù)據(jù)訪問層。
????在以往要在一個(gè)項(xiàng)目中動(dòng)態(tài)創(chuàng)建實(shí)體,我所知的方法是把要添加的實(shí)體放入一個(gè)程序集,然后通過反射加載程序集。現(xiàn)在可以通過動(dòng)態(tài)更改EDM的方法來增加實(shí)體并將其映射到數(shù)據(jù)庫,后者是以前無法實(shí)現(xiàn)的。
????便于更改數(shù)據(jù)庫,當(dāng)更換數(shù)據(jù)庫后,只需修改SSDL的定義,(如果數(shù)據(jù)庫的表明有變動(dòng),也只需多修改MSL),對CSDL沒有任何影響,從而也不需要對程序的BLL等上層部分做任何改動(dòng)。
- 條件
要想讓一個(gè)數(shù)據(jù)庫支持Entity Framework,一個(gè)必要條件就是該數(shù)據(jù)庫需提供相應(yīng)的Entity Client Data Provider,這樣才能將Entity SQL轉(zhuǎn)換為針對此數(shù)據(jù)此數(shù)據(jù)庫的SQL并交由ADO.NET來執(zhí)行。當(dāng)然該數(shù)據(jù)庫還需要提供ADO.NET Data Provider。
- 缺陷
Entity Framework技術(shù)的效率問題是其幾乎唯一一個(gè)稍有不足之處。首先其將EntitySQL轉(zhuǎn)換為SQL的方式屬于解釋性轉(zhuǎn)換,性能較差。另外Entity Framework在每次應(yīng)用啟動(dòng)時(shí)需要讀取EDM,這個(gè)過程較慢(但在后續(xù)操作時(shí),就不再存在這個(gè)問題)。
?
EDM中的DML
由于當(dāng)前的EntitySQL不支持DML操作,所以當(dāng)前版本的Entity Framework的插入、更新及刪除操作需要通過Object Service來完成。在EDM的設(shè)計(jì)器文件xxx.designer.cs中自動(dòng)生成了一些簽名為
void AddToEntity(EntityType entity)
的方法。我們只需要新建一個(gè)實(shí)體對象并調(diào)用這個(gè)方法添加實(shí)體即可。注意這個(gè)函數(shù)內(nèi)部調(diào)用
????entities.AddObject("EntitySetName", entity);
最后調(diào)用entities.SaveChanges()方法將修改保存回?cái)?shù)據(jù)庫,這是所有三種更新操作所需的。更新與刪除操作都需要先使用ObjectService定位操作的實(shí)體對象,更新操作直接使用賦值運(yùn)算符,刪除操作則調(diào)用
????entites.DeleteObject(object o);
方法。之后調(diào)用entities.SaveChanges()方法保存,這個(gè)過程簡單,不再贅述。
?
含有Association的EDM的使用
????當(dāng)前版本的Entity Framework不支持自動(dòng)延遲加載,所有當(dāng)前未使用的關(guān)系中的相關(guān)實(shí)體默認(rèn)按不加載處理,當(dāng)我們需要通過關(guān)系獲取一個(gè)實(shí)體對象時(shí),我們可以采用兩種方法:
實(shí)體框架針對 EntityReference 類的每個(gè)實(shí)例提供一個(gè) Load 方法。此方法可用于顯式加載與另一實(shí)體相關(guān)的一個(gè)集合。我們只需在訪問關(guān)系中實(shí)體之前調(diào)用其Load即可,當(dāng)然提前判斷該實(shí)體是否已經(jīng)加載是一種比較好的實(shí)踐。如下代碼所示
using (Entities entities = new Entities())
{
var query = (from o in entities.Orders
where o.Customers.CustomerID == "ALFKI"
select o);
foreach (Orders order in query)
{
if (!order.CustomersReference.IsLoaded)
order.CustomersReference.Load();
Console.WriteLine(order.OrderID + " --- " +
order.Customers.CompanyName);
}
}
先看代碼示例
using (Entities entities = new Entities())
{
var query = (from o in entities.Orders.Include("Customers")
where o.ShipCountry == "USA"
select o);
?
foreach (Orders order in query)
Console.WriteLine(order.OrderID + " --- " +
order.Customers.CompanyName);
}
查詢中針對 Orders 實(shí)體調(diào)用的 Include 方法接受了一個(gè)參數(shù),該參數(shù)在本示例中將要求查詢不僅要檢索 Orders,而且還要檢索相關(guān)的 Customers。這將生成單個(gè) SQL 語句,它會(huì)加載滿足 LINQ 查詢條件的所有 Order 和 Customer。
兩種加載關(guān)系實(shí)體的方式的選擇根據(jù):如果針對關(guān)系數(shù)據(jù)你只需做一到兩次查詢,則使用顯示加載更高效,如果要持續(xù)訪問關(guān)系實(shí)體中數(shù)據(jù),則使用預(yù)先加載。
?
關(guān)系下的添加,更新與刪除與上述操作基本相同,唯一需要注意的是刪除操作不支持級聯(lián)刪除,需要手工遍歷所有的相關(guān)項(xiàng)并將其一一刪除。注意這里刪除操作不能使用foreach來遍歷需要?jiǎng)h除的關(guān)系實(shí)體。取而代之的有兩種方法:
while (result.Order_Details.Count > 0)
{
// 刪除操作…
}
var items = result.Order_Details.ToList();
foreach (var item in items)
{
// 刪除操作…
}
最新補(bǔ)充
Entity Framework在開發(fā)中的應(yīng)用 – Entity Framework與控件
.NET Framework提供了許多xxxDataSource控件,如SqlDataSource,ObjectDataSource等,這些數(shù)據(jù)源控件大大方便了我們的數(shù)據(jù)綁定操作。不幸的是目前還沒有針對Entity Framework的數(shù)據(jù)源控件發(fā)布,但是將數(shù)據(jù)綁定到諸如ListBox,Grrdview或DetailsView控件也是很簡單的。這源于使用ObjectContext操作返回的IQueryable<T>對象或是使用EntityClient查詢返回的ObjectQuery對象都實(shí)現(xiàn)了IEnumerable接口。這樣很容易將這些數(shù)據(jù)綁定到數(shù)據(jù)顯示控件。更新操作可以按上文所述在相應(yīng)的時(shí)間處理函數(shù)中寫更新EDM的程序即可。
Entity Framework的鏈接字符串
????默認(rèn)情況下(Visual Studio對Entity Framework數(shù)據(jù)項(xiàng)目的默認(rèn)設(shè)置),EDM這個(gè)XML文件被作為資源在編譯時(shí)嵌入到程序集中。這種情況下當(dāng)更改EDM后需要重新編譯這個(gè)程序集才能使更改生效。通過更改項(xiàng)目屬性也可以讓EDM作為三個(gè)獨(dú)立的XML文件存在于項(xiàng)目中。為了讓應(yīng)用程序可以找到EDM(無論其以什么方式存儲(chǔ))需要一個(gè)鏈接字符串來指示EDM所在的位置。實(shí)體模型設(shè)計(jì)器生成的鏈接字符串如下所示:
| <add name="ASSEntities" connectionString=" metadata=res://*/ass.csdl| res://*/ass.ssdl| res://*/ass.msl; provider=System.Data.SqlClient; provider connection string="Data Source=(local);Initial Catalog=ASS;Integrated Security=True;MultipleActiveResultSets=True"" providerName="System.Data.EntityClient" /> |
http://msdn.microsoft.com/zh-cn/library/cc716756.aspx
關(guān)鍵的一點(diǎn)應(yīng)用程序是怎樣找到這個(gè)字符串的,對于使用EntityClient的情況,可以直接將連接字符串賦給EntityConnection的ConnectionString屬性,另外對于使用ObjectContext,其可以自動(dòng)由配置文件檢索這個(gè)連接字符串。
轉(zhuǎn)載于:https://www.cnblogs.com/vebest/archive/2010/06/03/1750685.html
總結(jié)
以上是生活随笔為你收集整理的[(转)hystar整理]Entity Framework 教程的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: LNK2005 连接错误解决办法
- 下一篇: What's blocking my l