程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> 關於.NET >> [hystar整理]Entity Framework 教程,hystarentity

[hystar整理]Entity Framework 教程,hystarentity

編輯:關於.NET

[hystar整理]Entity Framework 教程,hystarentity


參考頁面:

http://www.yuanjiaocheng.net/entity/entity-relations.html

http://www.yuanjiaocheng.net/entity/entity-lifecycle.html

http://www.yuanjiaocheng.net/entity/code-first.html

http://www.yuanjiaocheng.net/entity/mode-first.html

http://www.yuanjiaocheng.net/entity/database-first.html

本文檔主要介紹.NET開發中兩項新技術,.NET平台語言中的語言集成查詢技術 - LINQ,與ADO.NET中新增的數據訪問層設計技術ADO.NET Entity Framework。ADO.NET的LINQ to Entity部分以LINQ為基礎,為了完整性首先介紹LINQ技術。

預備知識

LINQ技術

LINQ是.NET 3.5中新增的一種技術,這個技術擴展了.NET平台上的編程語言,使其可以更加方便的進行數據查詢,單純的LINQ技術主要完成對集合對象(如System.Collection下或System.Collection.Generic命名空間下的對象)的查詢。結合LINQ Provider可以實現對XML文件(使用LINQ to XML – 位於System.Xml.Linq命名空間下的類),數據庫(可以使用LINQ to SQL或下文要詳細介紹的LINQ to Entity)等對象的操作。

 

LINQ是一種運行時無關的技術,其運行於CLR2.0之上,微軟對C#3.0與VB9.0的編譯器進性擴展,從而使其可以將LINQ編寫的程序編譯為可以被CLR2.0的JIT所理解的MSIL。

 

LINQ技術的基礎 - C#3.0

自動屬性

這個概念很簡單,其簡化了我們在.NET的時候手寫一堆私有成員+屬性的編程方式,我們只需要使用如下方式聲明一個屬性,編譯器會自動生成所需的成員變量。

1 public class Customer
2 {
3     public int Id { get; set; }
4     public string Name { get; set; }
5 }

在我使用LINQ完成的項目中,使我了解到自動屬性方便的一個用途如下:

在使用LINQ獲取數據的過程中,我們常常需要使用select new語句查詢出一個對象(往往是IEnumerable類型的)用於數據綁定。在一般情況下如果是直接綁定(如直接將查詢結果賦給一個Gridview控件的DataSource屬性)我們可以直接select new來返回一個匿名類的對象。如果我們還需要對這個集合對象進行進一步操作,我們將必須使用select new class-name這樣的語言返回一個類的對象,大部分情況下這個類只作為實體的一個結構而不需要完成一些操作操作,這時候使用自動屬性來完成這個類將是非常簡潔高效的。

隱式類型

這個名稱可能對你很陌生,但是var這個關鍵字應該都用過,在C#中使用var聲明一個對象時,編譯器會自動根據其賦值語句推斷這個局部變量的類型。賦值以後,這個變量的類型也就確定而不可以再進行更改。另外var關鍵字也用於匿名類的聲明。

應用場合:var主要用途是表示一個LINQ查詢的結果。這個結果可能是ObjectQuery<>或IQueryable<>類型的對象,也可能是一個簡單的實體類型的對象。這時使用var聲明這個對象可以節省很多代碼書寫上的時間。

對象初始化器與集合初始化器

在.NET2.0中構造一個對象的方法一是提供一個重載的構造函數,二是用默認的構造函數生成一個對象,然後對其屬性進行賦值。在.NET3.5/C#3.0中我們有一種更好的方式來進行對象的初始化。那就是使用對象初始化器。這個特性也是匿名類的一個基礎,所以放在匿名類之前介紹。

還是那就話,好的代碼強於注釋,下面用幾個代碼段說明初始化器:

(代碼出自:李永京的博客 http://lyj.cnblogs.com)

基本用法:

1 User user = new User { Id = 1, Name = "YJingLee", Age = 22 }; 

嵌套使用:

 1 User user = new User  2 {  3 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)的實體設計器對存儲過程支持不完善,只能手工編輯這三個文件中的存儲過程部分,包括:

下面分別描述一下有關修改操作的存儲過程的使用:

EntityContainerMapping

   

EntitySetMapping

EntityContainer中每個EntitySet的對應

 

EntityTypeMapping

描述CSDL中EntityType與SSDL中EntityType的對應

 

ModificationFunctionMapping

CUD對應的存儲過程

 

InsertFunction/ UpdateFunction / DeleteFunction

FunctionName

 

 

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這種使用方式下,使用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下使用EntitySQL的一個主要作用,上面的例子比較簡單可能看不到這樣使用的優勢,但是如下兩種情況下使用EntitySQL可能是最好的選擇。

 

    使用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與圖2、圖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不支持自動延遲加載,所有當前未使用的關系中的相關實體默認按不加載處理,當我們需要通過關系獲取一個實體對象時,我們可以采用兩種方法:

兩種加載關系實體的方式的選擇根據:如果針對關系數據你只需做一到兩次查詢,則使用顯示加載更高效,如果要持續訪問關系實體中數據,則使用預先加載。

關系下的添加,更新與刪除與上述操作基本相同,唯一需要注意的是刪除操作不支持級聯刪除,需要手工遍歷所有的相關項並將其一一刪除。注意這裡刪除操作不能使用foreach來遍歷需要刪除的關系實體。取而代之的有兩種方法:

最新補充

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=&quot;Data Source=(local);Initial Catalog=ASS;Integrated Security=True;MultipleActiveResultSets=True&quot;" 
8 providerName="System.Data.EntityClient" />

http://msdn.microsoft.com/zh-cn/library/cc716756.aspx

關鍵的一點應用程序是怎樣找到這個字符串的,對於使用EntityClient的情況,可以直接將連接字符串賦給EntityConnection的ConnectionString屬性,另外對於使用ObjectContext,其可以自動由配置文件檢索這個連接字符串。

 

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved