目錄
Entity Framework的核心 – EDM(Entity Data Model) 7
為什麼要使用Entity Framework,限制條件及當前版本框架的問題 23
本文件主要介紹.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
-
自動屬性
-
隱式型別
-
物件集合初始化器
-
匿名類
-
擴充套件方法
-
Lambda表示式
自動屬性
這個概念很簡單,其簡化了我們在.NET的時候手寫一堆私有成員+屬性的程式設計方式,我們只需要使用如下方式宣告一個屬性,編譯器會自動生成所需的成員變數。
public class Customer
{
public int Id { get; set; }
public string Name { get; set; }
}
在我使用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)
基本用法:
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
}
};
類似於物件初始化器初始化一個物件,集合初始化器初始化一個集合,一句話,有了它你就不用在將元素通過Add逐個新增了。仍然給出程式碼示例:
基本使用:
List<int> num = new List<int> { 0, 1, 2, 6, 7, 8, 9 };
結合物件初始化器,我們可以寫出如下簡潔的程式碼:
List<User> user = new List<User>{
new User{Id=1,Name="YJingLee",Age=22},
new User{Id=2,Name="XieQing",Age=25},
};
應用場合:
還是前文提到的select new class-name語法,後面可以直接接一個初始化器來將查詢結果返回到這個物件。
匿名類
有了前文初始化器的介紹,匿名類就很簡單了。我們可以使用new { object initializer }或new[]{ object, …}來初始化一個匿名類或不確定型別的陣列。匿名類的物件需要使用var關鍵字宣告。示例程式碼:
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)
public static class Extensions
{
public static bool IsValidEmailAddress(this string s)
{
Regex regex = new Regex(@"^[/w-/.]+@([/w-]+/.)+[/w-]{2,4}$");
return regex.IsMatch(s);
}
}
如上程式碼所示,擴充套件方法為一靜態方法,宣告於一個靜態類,其引數前加上一個this關鍵字,引數的型別表示這個擴充套件方法要對這個型別進行擴充套件。如上述程式碼表示其要對字串型別進行擴充套件。
在應用上擴充套件方法被作為其擴充套件的型別的靜態方法來呼叫。如下:
if (email.IsValidEmailAddress())
{
Response.Write("YJingLee提示:這是一個正確的郵件地址");
}
Lambda表示式
Lambda表示式是對.NET2.0中匿名方法在語法形式上的進一步改進,仍然以程式碼說明:
var inString = list.FindAll(delegate(string s) { return s.Indexof("YJingLee") >= 0; });
使用Lambda表示式程式碼將更自然易懂。
var inString = list.FindAll(s => s.Indexof("YJingLee") >= 0);
可以看出,Lambda表示式格式為:(引數列表)=>表示式或語句塊
另外我對於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形式:
這個設計器生成的檔案的註釋可以使你很清楚的明白這個EDM檔案的組成。一點點分析一下,第一行表明這是一個xml檔案。
<?xml version="1.0" encoding="utf-8"?> |
以下這一行是EDM的根節點,定義了一個表明版本的屬性及這個EDM使用的名稱空間:
<edmx:Edmx Version="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來自作業提交系統(有刪節):
<!-- 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> <!-- 以上刪節 – 5個儲存過程 -->
<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> <!-- 以上刪節 – 6個關係集 -->
</EntityContainer>
<!-- 以下保留一個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對應的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指定一個易記的名稱,在定義Alias之後,在此Schema內的Element均可以該Alias作為Namespace的別名。Alias的使用可以參考如下xml element:
<FunctionImport Name="GETHOUSEWORKDONE" EntitySet="UpAssignments" ReturnType="Collection(Self.UpAssignments)"> |
在這個根元素的內部的文件結構第一部分 – 實體容器大致如下:
<EntityContainer /> <FunctionImport /> <EntitySet /> <AssociationSet /> </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:
<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>
下表說明了其屬性及其子節點與子節點的屬性的含義:
EntityType |
|||
Name |
Entity Class的名稱 |
||
Abstract |
是否為抽象類 |
||
BaseType |
父類 |
||
Key |
主鍵 |
||
Property |
主鍵之屬性 |
||
Name |
屬性名 |
||
Property |
屬性 |
||
Name |
屬性名 |
||
Type |
屬性型別 |
||
Nullable |
是否允許null |
||
MaxLength |
屬性最大長度 |
||
FixLength |
是否固定長度 |
||
NavigationProperty |
關係屬性 |
||
Name |
屬性名 |
||
Relationship |
對應的Association |
||
FromRole、ToRole |
區別關係兩方的父與子 |
最後Association節,這是真正定義關係的地方。首先看示例:
<!-- 僅保留與上文AssociationSet對應的Association -->
<Association Name="FK_SetCourses_Classes">
<End Role="Classes" Type="ASSModel.Classes" Multiplicity="1" />
<End Role="SetCourses" Type="ASSModel.SetCourses" Multiplicity="*" />
</Association>
這一節符合以下結構:
<Association> <End /> <ReferentialConstraint> <Principal> <PropertyRef /> </Principal> <Dependent> <PropertyRef /> </Dependent> </ReferentialConstraint> </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
這個檔案中描述了表、列、關係、主鍵及索引等資料庫中存在的概念。
<!-- 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個EntitySet的定義 --> </EntityContainer>
<!-- 以下省略7個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中對應的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> |
看文件的結構,SSDL與CSDL很詳細,只是其中EntityType等使用資料庫的概念的描述。
這其中有一個需要稍微介紹節點,DefiningQuery,首先看一下其出現的位置:
EntityContainer |
||||
EntitySet |
||||
DefiningQuery |
通過查詢定義一個SSDL的EntitySet |
|||
特定於儲存的查詢語句 |
DefiningQuery定義通過實體資料模型 (EDM) 內的客戶端投影對映到資料儲存檢視的查詢。此類對映是隻讀的。也就是說如果想要更新此類EntitySet,需要使用下文介紹儲存過程時提到的定義更新實體的儲存過程的方法,使用定義的儲存過程來更新這樣的EntitySet。當在實體類設計器中匯入無主鍵的表時,會自動生成此類使用DefiningQuery定義的EntitySet,要式樣Entity Framework提供的自動更新服務而不定義儲存過程,需要給資料表新增一個適當的主鍵,刪除DefiningQuery節點並更新資料模型。
EDM之MSL
這個檔案即上面所述的CSDL與SSDL的對應,主要包括CSDL中屬性與SSDL中列的對應。
<!-- 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若干 -->
<!-- 保留對應於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的根節點為Mapping,其中可以包含多個EntityContainerMapping(上例只有一個),每一個EntityContainerMapping對應著兩個分別來自CSDL與SSDL的EntityContainer。這個EntityContainerMapping就是描述這兩個EntityContainer間的對應。下面再給出一段程式碼展示EntityContainerMapping的基本格式。
<EntityContainerMapping StorageEntityContainer="" CdmEntityContainer=""> <EntitySetMapping> <EntityTypeMapping> <MappingFragment> <ScalarProperty /> </MappingFragment> <ModificationFunctionMapping> <InsertFunction /> <DeleteFunction /> <UpdateFunction /> </ ModificationFunctionMapping> </EntityTypeMapping> </EntitySetMapping>
<AssociationSetMapping> <EndProperty> <ScalarProperty /> </EndProperty> </AssociationSetMapping>
<FunctionImportMapping /> </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>節同級,其結構如下:
<ComplexType> <Property /> </ComplexType> |
節點及其屬性含義如下:
ComplexType |
複雜型別 |
|
Name |
複雜型別的名稱 |
|
Property |
屬性 |
|
Name |
屬性名 |
|
Type |
屬性型別 |
|
Nullable |
是否允許null |
|
MaxLength |
屬性最大長度 |
|
FixLength |
是否固定長度 |
然後在MSL中<MappingFragment>下與<ScalarProperty />同級的位置新增如下節:
<ComplexProperty> <ScalarProperty /> </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
示例程式碼:
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();
}
-
ObjectService+EntitySQL
在有EntityClient+EntitySQL這種使用方式下,使用ObjectService+EntitySQL的方式是多此一舉,不會得到任何編輯時或執行時的好處。在ObjectContext下使用EntitySQL的真正作用是將其與LINQ to Entity結合使用。具體可見下文所示。
示例程式碼:
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);
}
-
ObjectContext+LINQ( to Entity)
方式一:
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的寫法隱式呼叫了2中示例的ObjectQuery<Customers>來進行查詢(關於此可以參見EDM的設計器檔案-xxx.designer.cs)。在方式二中的Where方法傳入的是一個Lambda表示式,你也可以傳入一條EntitySQL語句做引數來將LINQ與EntitySQL結合使用。如下程式碼演示其使用:
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的一個主要作用,上面的例子比較簡單可能看不到這樣使用的優勢,但是如下兩種情況下使用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。
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 |
驗證 Entity Model。
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不支援自動延遲載入,所有當前未使用的關係中的相關實體預設按不載入處理,當我們需要通過關係獲取一個實體物件時,我們可以採用兩種方法:
-
顯示載入
實體框架針對 EntityReference 類的每個例項提供一個 Load 方法。此方法可用於顯式載入與另一實體相關的一個集合。我們只需在訪問關係中實體之前呼叫其Load即可,當然提前判斷該實體是否已經載入是一種比較好的實踐。如下程式碼所示
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 實體呼叫的 Include 方法接受了一個引數,該引數在本示例中將要求查詢不僅要檢索 Orders,而且還要檢索相關的 Customers。這將生成單個 SQL 語句,它會載入滿足 LINQ 查詢條件的所有 Order 和 Customer。
兩種載入關係實體的方式的選擇根據:如果針對關係資料你只需做一到兩次查詢,則使用顯示載入更高效,如果要持續訪問關係實體中資料,則使用預先載入。
關係下的新增,更新與刪除與上述操作基本相同,唯一需要注意的是刪除操作不支援級聯刪除,需要手工遍歷所有的相關項並將其一一刪除。注意這裡刪除操作不能使用foreach來遍歷需要刪除的關係實體。取而代之的有兩種方法:
-
while法
while (result.Order_Details.Count > 0)
{
// 刪除操作…
}
-
ToList法(以非聯機方式操作)
var items = result.Order_Details.ToList();
foreach (var item in items)
{
// 刪除操作…
}
最新補充
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所在的位置。實體模型設計器生成的連結字串如下所示:
<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
關鍵的一點應用程式是怎樣找到這個字串的,對於使用EntityClient的情況,可以直接將連線字串賦給EntityConnection的ConnectionString屬性,另外對於使用ObjectContext,其可以自動由配置檔案檢索這個連線字串。