本文檔主要介紹.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,其能夠自動由配置文件檢索這個鏈接字符串。