前言
Entity Framework技術是通過SSDL(Storage Schema Definition Language)、CSDL(Conceptual Schema Definition Language)和MSL(Mapping Concept and Storage Schema Language)三層映射語言來實現對象-關係映射的。下圖展示了Entity Framework對象-關係映射技術架構:
圖1實體數據模型內容結構圖
E-R模型
本文示例使用的E-R模型如下圖所示:
圖2 E-R模型圖
對應的DDL語句如下:
1 --創建表 2 CREATE TABLE [Role]( 3 [ID] [uniqueidentifier] NOT NULL, 4 [Name] [nvarchar](40) NOT NULL, 5 [ParentID] [uniqueidentifier] NULL) 6 GO 7 8 CREATE TABLE [User]( 9 [ID] [uniqueidentifier] NOT NULL, 10 [Account] [nvarchar](50) NOT NULL, 11 [Password] [varchar](50) NOT NULL) 12 GO 13 14 CREATE TABLE [UserDetail]( 15 [ID] [uniqueidentifier] NOT NULL, 16 [Name] [nvarchar](50) NOT NULL, 17 [Sex] [nvarchar](2) NULL, 18 [Birthday] [datetime] NULL) 19 GO 20 21 CREATE TABLE [UserRole]( 22 [UserID] [uniqueidentifier] NOT NULL, 23 [RoleID] [uniqueidentifier] NOT NULL) 24 GO 25 26 --創建主鍵 27 ALTER TABLE [Role] 28 ADD CONSTRAINT [PK_Role] 29 PRIMARY KEY CLUSTERED ([ID] ASC); 30 GO 31 32 ALTER TABLE [User] 33 ADD CONSTRAINT [PK_User] 34 PRIMARY KEY CLUSTERED ([ID] ASC); 35 GO 36 37 ALTER TABLE [UserDetail] 38 ADD CONSTRAINT [PK_UserDetail] 39 PRIMARY KEY CLUSTERED ([ID] ASC); 40 GO 41 42 ALTER TABLE [UserRole] 43 ADD CONSTRAINT [PK_UserRole] 44 PRIMARY KEY CLUSTERED ([UserID] ASC, RoleID ASC); 45 GO 46 47 --創建外鍵 48 ALTER TABLE [Role] WITH CHECK ADD CONSTRAINT [FK_Role_Role] FOREIGN KEY([ParentID]) 49 REFERENCES [Role] ([ID]) 50 GO 51 52 ALTER TABLE [UserDetail] WITH CHECK ADD CONSTRAINT [FK_UserDetail_User] FOREIGN KEY([ID]) 53 REFERENCES [User] ([ID]) 54 ON DELETE CASCADE 55 GO 56 57 ALTER TABLE [UserRole] WITH CHECK ADD CONSTRAINT [FK_UserRole_User] FOREIGN KEY([UserID]) 58 REFERENCES [User] ([ID]) 59 ON DELETE CASCADE 60 GO 61 62 ALTER TABLE [UserRole] WITH CHECK ADD CONSTRAINT [FK_UserRole_Role] FOREIGN KEY([RoleID]) 63 REFERENCES [Role] ([ID]) 64 ON DELETE CASCADE 65 GO 66 67 --創建儲存過程 68 --SQL Server自關聯表無法通過外鍵設置級聯刪除,所以專門寫一個觸發器來完成該工作 69 CREATE TRIGGER TRG_Role_Delete 70 ON [Role] 71 INSTEAD OF DELETE 72 AS 73 BEGIN 74 -- SET NOCOUNT ON added to prevent extra result sets from 75 -- interfering with SELECT statements. 76 SET NOCOUNT ON; 77 78 DECLARE @ID uniqueidentifier; 79 SELECT @ID = ID FROM deleted; 80 81 DELETE [Role] WHERE ParentID = @ID; 82 DELETE [Role] WHERE ID = @ID; 83 END 84 GO
使用SQL Server Management Studio工具創建名爲“Membership”的數據庫,並執行以上的DDL語句,建立本示例所需的數據庫對象。
實體數據模型
在Visual Studio解決方案的Edm項目中,使用Database First開發模式生成實體數據模型,如下圖所示:
圖3自動生成的實體數據模型
可以看到,自動生成的實體數據模型包括兩個文件:Membership.edmx和Membership.Designer.cs。
Membership.edmx是使用XML語言進行描述的,在Visual Studio中,用XML(文本)編輯器打開Membership.edmx文件,可以看到該文件的內容結構如下圖所示:
圖4 Membership.edmx文件內容結構圖
上圖清晰地展示了Membership.edmx文件包括四部分內容,分別是SSDL、CSDL、MSL和設計視圖內容。設計視圖內容部分主要提供實體數據模型可視化編輯工具所需的實體圖像的尺寸、位置等信息,本文對此就不做進一步研究了。Membership.edmx是對象-關係映射文件,它是實體數據模型的關鍵內容。
Membership.Designer.cs是Visual Studio運用T4模板技術,根據CSDL內容生成的後臺代碼,其內容如下圖所示:
圖5 Membership.Designer.cs文件內容結構圖
Membership.Designer.cs文件包括上下文和實體兩部分內容,對應圖1中所示的ObjectContext和Entity。它是上層代碼訪問實體對象模型的入口,有了它才使實體對象模型變得可用和易用。
一、SSDL
SSDL(Store Schema Definition Language,存儲架構定義語言)面向關係,定義數據庫對象的架構信息,包括表結構、主外鍵信息,以及存儲過程等。該部分內容根據不同的數據庫,會有一定的差異。Membership.edmx文件中SSDL部分的內容如下:
1 <edmx:StorageModels> 2 <Schema Namespace="MembershipModel.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/2009/02/edm/ssdl"> 3 <EntityContainer Name="MembershipModelStoreContainer"> 4 <EntitySet Name="Role" EntityType="MembershipModel.Store.Role" store:Type="Tables" Schema="dbo" /> 5 … 6 <AssociationSet Name="FK_Role_Role" Association="MembershipModel.Store.FK_Role_Role"> 7 <End Role="Parent" EntitySet="Role" /> 8 <End Role="Children" EntitySet="Role" /> 9 </AssociationSet> 10 … 11 </EntityContainer> 12 <EntityType Name="Role"> 13 <Key> 14 <PropertyRef Name="ID" /> 15 </Key> 16 <Property Name="ID" Type="uniqueidentifier" Nullable="false" /> 17 <Property Name="Name" Type="nvarchar" Nullable="false" MaxLength="40" /> 18 <Property Name="ParentID" Type="uniqueidentifier" /> 19 </EntityType> 20 … 21 <Association Name="FK_Role_Role"> 22 <End Role="Parent" Type="MembershipModel.Store.Role" Multiplicity="0..1" /> 23 <End Role="Children" Type="MembershipModel.Store.Role" Multiplicity="*" /> 24 <ReferentialConstraint> 25 <Principal Role="Parent"> 26 <PropertyRef Name="ID" /> 27 </Principal> 28 <Dependent Role="Children"> 29 <PropertyRef Name="ParentID" /> 30 </Dependent> 31 </ReferentialConstraint> 32 </Association> 33 … 34 </Schema> 35 </edmx:StorageModels>
由上面的代碼示例可以看出,SSDL的內容主要包括兩大部分:對象容器和對象詳細定義。而這兩部分均包括了對象(表)和關係(外鍵)兩方面的內容。
二、CSDL
CSDL(Conceptual Schema Definition Language,概念架構定義語言)面向對象,定義概念層實體對象的架構信息,包括實體屬性信息及實體間的引用關係。該部分不關心下層所使用的數據庫類型,內容始終保持一致。Membership.edmx文件中CSDL部分的內容如下:
1 <edmx:ConceptualModels> 2 <Schema Namespace="MembershipModel" Alias="Self" xmlns:annotation="http://schemas.microsoft.com/ado/2009/02/edm/annotation" xmlns="http://schemas.microsoft.com/ado/2008/09/edm"> 3 <EntityContainer Name="Membership" annotation:LazyLoadingEnabled="true"> 4 <EntitySet Name="Roles" EntityType="MembershipModel.Role" /> 5 … 6 <AssociationSet Name="FK_Role_Role" Association="MembershipModel.FK_Role_Role"> 7 <End Role="Parent" EntitySet="Roles" /> 8 <End Role="Children" EntitySet="Roles" /> 9 </AssociationSet> 10 … 11 </EntityContainer> 12 <EntityType Name="Role"> 13 <Key> 14 <PropertyRef Name="ID" /> 15 </Key> 16 <Property Name="ID" Type="Guid" Nullable="false" /> 17 <Property Name="Name" Type="String" Nullable="false" MaxLength="40" Unicode="true" FixedLength="false" /> 18 <Property Name="ParentID" Type="Guid" /> 19 <NavigationProperty Name="Parent" Relationship="MembershipModel.FK_Role_Role" FromRole="Children" ToRole="Parent" /> 20 <NavigationProperty Name="Children" Relationship="MembershipModel.FK_Role_Role" FromRole="Parent" ToRole="Children" /> 21 … 22 </EntityType> 23 … 24 <Association Name="FK_Role_Role"> 25 <End Role="Parent" Type="MembershipModel.Role" Multiplicity="0..1" /> 26 <End Role="Children" Type="MembershipModel.Role" Multiplicity="*" /> 27 <ReferentialConstraint> 28 <Principal Role="Parent"> 29 <PropertyRef Name="ID" /> 30 </Principal> 31 <Dependent Role="Children"> 32 <PropertyRef Name="ParentID" /> 33 </Dependent> 34 </ReferentialConstraint> 35 </Association> 36 … 37 </Schema> 38 </edmx:ConceptualModels>
由上面的代碼示例可以看出,CSDL的內容與SSDL其實很類似,主要包括實體容器和實體引用關係兩部分。所不同的是,CSDL是針對具體某種數據庫的E-R模型進行定義,而CSDL則是針對抽象的實體概念模型進行定義。
三、MSL
MSL(Mapping Specification Language,映射規範語言)實現SSDL和CSDL之間的映射,包括表與實體的映射、字段與屬性的映射,以及外鍵與引用的映射等。Membership.edmx文件中MSL部分的內容如下:
1 <edmx:Mappings> 2 <Mapping Space="C-S" xmlns="http://schemas.microsoft.com/ado/2008/09/mapping/cs"> 3 <EntityContainerMapping StorageEntityContainer="MembershipModelStoreContainer" CdmEntityContainer="Membership"> 4 <EntitySetMapping Name="Roles"> 5 <EntityTypeMapping TypeName="MembershipModel.Role"> 6 <MappingFragment StoreEntitySet="Role"> 7 <ScalarProperty Name="ID" ColumnName="ID" /> 8 <ScalarProperty Name="Name" ColumnName="Name" /> 9 <ScalarProperty Name="ParentID" ColumnName="ParentID" /> 10 </MappingFragment> 11 </EntityTypeMapping> 12 </EntitySetMapping> 13 … 14 <AssociationSetMapping Name="UserRole" TypeName="MembershipModel.UserRole" StoreEntitySet="UserRole"> 15 <EndProperty Name="Role"> 16 <ScalarProperty Name="ID" ColumnName="RoleID" /> 17 </EndProperty> 18 <EndProperty Name="User"> 19 <ScalarProperty Name="ID" ColumnName="UserID" /> 20 </EndProperty> 21 </AssociationSetMapping> 22 … 23 </EntityContainerMapping> 24 </Mapping> 25 </edmx:Mappings>
由上面的代碼示例可以看出,MSL的內容主要是定義SSDL與CSDL的映射關係,其內容主要包括對象與實體、關係與引用的映射。
四、實體
實體是E-R關係在概念層的映射表現,Visual Studio自動生成的實體並不是POCO對象,而是繼承自EntityObject對象的分部類,其內容大致如下:
1 [EdmEntityTypeAttribute(NamespaceName="MembershipModel", Name="Role")] 2 [Serializable()] 3 [DataContractAttribute(IsReference=true)] 4 public partial class Role : EntityObject 5 { 6 #region 工廠方法 7 public static Role CreateRole(global::System.Guid id, global::System.String name) 8 { 9 Role role = new Role(); 10 role.ID = id; 11 role.Name = name; 12 return role; 13 } 14 #endregion 15 16 #region 基元屬性 17 [EdmScalarPropertyAttribute(EntityKeyProperty=true, IsNullable=false)] 18 [DataMemberAttribute()] 19 public global::System.Guid ID 20 { 21 get { return _ID; } 22 set 23 { 24 if (_ID != value) 25 { 26 OnIDChanging(value); 27 ReportPropertyChanging("ID"); 28 _ID = StructuralObject.SetValidValue(value); 29 ReportPropertyChanged("ID"); 30 OnIDChanged(); 31 } 32 } 33 } 34 private global::System.Guid _ID; 35 partial void OnIDChanging(global::System.Guid value); 36 partial void OnIDChanged(); 37 38 … 39 40 [EdmScalarPropertyAttribute(EntityKeyProperty=false, IsNullable=true)] 41 [DataMemberAttribute()] 42 public Nullable<global::System.Guid> ParentID 43 { 44 get { return _ParentID; } 45 set 46 { 47 OnParentIDChanging(value); 48 ReportPropertyChanging("ParentID"); 49 _ParentID = StructuralObject.SetValidValue(value); 50 ReportPropertyChanged("ParentID"); 51 OnParentIDChanged(); 52 } 53 } 54 private Nullable<global::System.Guid> _ParentID; 55 partial void OnParentIDChanging(Nullable<global::System.Guid> value); 56 partial void OnParentIDChanged(); 57 #endregion 58 59 #region 導航屬性 60 [XmlIgnoreAttribute()] 61 [SoapIgnoreAttribute()] 62 [DataMemberAttribute()] 63 [EdmRelationshipNavigationPropertyAttribute("MembershipModel", "FK_Role_Role", "Children")] 64 public EntityCollection<Role> Children 65 { 66 get 67 { 68 return ((IEntityWithRelationships)this).RelationshipManager.GetRelatedCollection<Role>("MembershipModel.FK_Role_Role", "Children"); 69 } 70 set 71 { 72 if ((value != null)) 73 { 74 ((IEntityWithRelationships)this).RelationshipManager.InitializeRelatedCollection<Role>("MembershipModel.FK_Role_Role", "Children", value); 75 } 76 } 77 } 78 79 [XmlIgnoreAttribute()] 80 [SoapIgnoreAttribute()] 81 [DataMemberAttribute()] 82 [EdmRelationshipNavigationPropertyAttribute("MembershipModel", "FK_Role_Role", "Parent")] 83 public Role Parent 84 { 85 get 86 { 87 return ((IEntityWithRelationships)this).RelationshipManager.GetRelatedReference<Role>("MembershipModel.FK_Role_Role", "Parent").Value; 88 } 89 set 90 { 91 ((IEntityWithRelationships)this).RelationshipManager.GetRelatedReference<Role>("MembershipModel.FK_Role_Role", "Parent").Value = value; 92 } 93 } 94 95 [BrowsableAttribute(false)] 96 [DataMemberAttribute()] 97 public EntityReference<Role> ParentReference 98 { 99 get 100 { 101 return ((IEntityWithRelationships)this).RelationshipManager.GetRelatedReference<Role>("MembershipModel.FK_Role_Role", "Parent"); 102 } 103 set 104 { 105 if ((value != null)) 106 { 107 ((IEntityWithRelationships)this).RelationshipManager.InitializeRelatedReference<Role>("MembershipModel.FK_Role_Role", "Parent", value); 108 } 109 } 110 } 111 … 112 #endregion 113 }
由上面的代碼可以看出,實體類主要包括工廠方法、基元屬性和導航屬性三部分內容。其中基元屬性和導航屬性與CSDL中定義的實體內容基本一致。此外,實體類還附加了很多特性、事件方法等,在本系列的第5篇延遲加載中,你將瞭解到所有這些附件的內容,幾乎都是用於實現自動延遲加載功能的。
五、ObjectContext
ObjectContext是實體上下文環境,自定義上下文環境需要繼承該類,並擴展自定義的實體集,以供上層代碼通過它方便的使用實體。自定義ObjectContext類的內容如下:
1 public partial class Membership : ObjectContext 2 { 3 #region 構造函數 4 public Membership() : base("name=Membership", "Membership") 5 { 6 this.ContextOptions.LazyLoadingEnabled = true; 7 } 8 9 public Membership(string connectionString) : base(connectionString, "Membership") 10 { 11 this.ContextOptions.LazyLoadingEnabled = true; 12 } 13 14 public Membership(EntityConnection connection) : base(connection, "Membership") 15 { 16 this.ContextOptions.LazyLoadingEnabled = true; 17 } 18 #endregion 19 20 #region ObjectSet 屬性 21 22 public ObjectSet<Role> Roles 23 { 24 get 25 { 26 if ((_Roles == null)) 27 { 28 _Roles = base.CreateObjectSet<Role>("Roles"); 29 } 30 return _Roles; 31 } 32 } 33 private ObjectSet<Role> _Roles; 34 … 35 #endregion 36 37 #region AddTo 方法 38 /// </summary> 39 public void AddToRoles(Role role) 40 { 41 base.AddObject("Roles", role); 42 } 43 … 44 #endregion 45 }
由上面的代碼可以看出,自定義實體上下文環境類的內容主要包括構造函數、實體集和AddTo方法。其中,構造函數提供使用默認連接配置名、指定連接配置名和指定連接三種方式構造實例;實體集定義該上下文環境可以訪問的實體集合,這部分很重要,上層代碼都是通過這部分提供的實體集來訪問實體對象模型的;AddTo方法是向實體集中新增實體,這部分內容是已經廢棄了的,我們可以直接使用實體集的添加方法替換。
另外,還有一部分內容是容易被忽略的,那就是關係源元數據,可以在Membership.Designer.cs文件的開始部分找到它,內容如下:
1 [assembly: EdmSchemaAttribute()] 2 #region EDM 關係源元數據 3 4 [assembly: EdmRelationshipAttribute("MembershipModel", "FK_Role_Role", "Parent", System.Data.Metadata.Edm.RelationshipMultiplicity.ZeroOrOne, typeof(Apollo.Blog.EF.Chapter3.Edm.Role), "Children", System.Data.Metadata.Edm.RelationshipMultiplicity.Many, typeof(Apollo.Blog.EF.Chapter3.Edm.Role), true)] 5 [assembly: EdmRelationshipAttribute("MembershipModel", "FK_UserDetail_User", "User", System.Data.Metadata.Edm.RelationshipMultiplicity.One, typeof(Apollo.Blog.EF.Chapter3.Edm.User), "UserDetail", System.Data.Metadata.Edm.RelationshipMultiplicity.ZeroOrOne, typeof(Apollo.Blog.EF.Chapter3.Edm.UserDetail), true)] 6 [assembly: EdmRelationshipAttribute("MembershipModel", "UserRole", "Role", System.Data.Metadata.Edm.RelationshipMultiplicity.Many, typeof(Apollo.Blog.EF.Chapter3.Edm.Role), "User", System.Data.Metadata.Edm.RelationshipMultiplicity.Many, typeof(Apollo.Blog.EF.Chapter3.Edm.User))] 7 8 #endregion
關係源元數據定義了實體對象間引用關係的元數據信息,它是實現自動延遲加載不可或缺的一部分,本系列的第5篇文章將會涉及到該部分內容。
實體數據模型的內容就這麼多了。下面提供圖1的形象化版本,以幫助你更直觀的瞭解對象-關係映射的實現過程:
圖6對象-關係映射過程圖
關係映射
衆所周知,E-R關係主要包括一對一、一對多和多對多三種,在瞭解了對象-關係映射的實現過程後,下面就逐一對這三種關係與實體數據模型中的實體引用如何進行映射做進一步的研究。考慮篇幅過長會影響讀者對重點內容的關注,後續代碼中只摘出關鍵的、能說明問題的部分進行闡述。
一、一對一
本文示例所用的E-R模型(如圖2)中,User和UserDetail對象是一對一的關係。接下來就來看看這兩個對象及其關係是如何映射爲對應的實體對象模型的。
首先來看SSDL定義,如下所示:
1 <edmx:StorageModels> 2 <Schema> 3 <EntityContainer Name="MembershipModelStoreContainer"> 4 … 5 <AssociationSet Name="FK_UserDetail_User" Association="MembershipModel.Store.FK_UserDetail_User"> 6 <End Role="User" EntitySet="User" /> 7 <End Role="UserDetail" EntitySet="UserDetail" /> 8 </AssociationSet> 9 </EntityContainer> 10 <EntityType Name="User"> 11 <Key> 12 <PropertyRef Name="ID" /> 13 </Key> 14 <Property Name="ID" Type="uniqueidentifier" Nullable="false" /> 15 … 16 </EntityType> 17 <EntityType Name="UserDetail"> 18 <Key> 19 <PropertyRef Name="ID" /> 20 </Key> 21 <Property Name="ID" Type="uniqueidentifier" Nullable="false" /> 22 … 23 </EntityType> 24 <Association Name="FK_UserDetail_User"> 25 <End Role="User" Type="MembershipModel.Store.User" Multiplicity="1" /> 26 <End Role="UserDetail" Type="MembershipModel.Store.UserDetail" Multiplicity="0..1" /> 27 <ReferentialConstraint> 28 <Principal Role="User"> 29 <PropertyRef Name="ID" /> 30 </Principal> 31 <Dependent Role="UserDetail"> 32 <PropertyRef Name="ID" /> 33 </Dependent> 34 </ReferentialConstraint> 35 </Association> 36 </Schema> 37 </edmx:StorageModels>
SSDL的定義包含了User表、UserDetail表和FK_UserDetail_User外鍵的定義,與前文給定的數據庫DDL的定義是一一對應的,不用做過多的解釋了。
CSDL定義如下所示:
1 <edmx:ConceptualModels> 2 <Schema> 3 <EntityContainer Name="Membership" annotation:LazyLoadingEnabled="true"> 4 … 5 <AssociationSet Name="FK_UserDetail_User" Association="MembershipModel.FK_UserDetail_User"> 6 <End Role="User" EntitySet="Users" /> 7 <End Role="UserDetail" EntitySet="UserDetails" /> 8 </AssociationSet> 9 </EntityContainer> 10 <EntityType Name="User"> 11 … 12 <NavigationProperty Name="UserDetail" Relationship="MembershipModel.FK_UserDetail_User" FromRole="User" ToRole="UserDetail" /> 13 </EntityType> 14 <EntityType Name="UserDetail"> 15 … 16 <NavigationProperty Name="User" Relationship="MembershipModel.FK_UserDetail_User" FromRole="UserDetail" ToRole="User" /> 17 </EntityType> 18 <Association Name="FK_UserDetail_User"> 19 <End Role="User" Type="MembershipModel.User" Multiplicity="1" /> 20 <End Role="UserDetail" Type="MembershipModel.UserDetail" Multiplicity="0..1" /> 21 <ReferentialConstraint> 22 <Principal Role="User"> 23 <PropertyRef Name="ID" /> 24 </Principal> 25 <Dependent Role="UserDetail"> 26 <PropertyRef Name="ID" /> 27 </Dependent> 28 </ReferentialConstraint> 29 </Association> 30 </Schema> 31 </edmx:ConceptualModels>
CSDL中也是對實體和引用進行了定義。另外,在實體定義中,加入了導航屬性來實現實體引用。這種引用關係是雙向的,即你可以在User實體中引用UserDetail實體,也可以在UserDetail實體中引用User實體。其實不只是一對一,一對多、多對多映射也是同樣的情況,下文就不再贅述了。
MSL中不需要對一對一映射作任何定義,只是簡單的定義表與實體、字段與屬性映射關係即可。
關係源元數據如下所示:
1 [assembly: EdmRelationshipAttribute("MembershipModel", "FK_UserDetail_User", "User", System.Data.Metadata.Edm.RelationshipMultiplicity.One, typeof(Apollo.Blog.EF.Chapter3.Edm.User), "UserDetail", System.Data.Metadata.Edm.RelationshipMultiplicity.ZeroOrOne, typeof(Apollo.Blog.EF.Chapter3.Edm.UserDetail), true)]
實體的內容如下所示:
1 public partial class User : EntityObject 2 { 3 #region 導航屬性 4 [XmlIgnoreAttribute()] 5 [SoapIgnoreAttribute()] 6 [DataMemberAttribute()] 7 [EdmRelationshipNavigationPropertyAttribute("MembershipModel", "FK_UserDetail_User", "UserDetail")] 8 public UserDetail UserDetail 9 { 10 get 11 { 12 return ((IEntityWithRelationships)this).RelationshipManager.GetRelatedReference<UserDetail>("MembershipModel.FK_UserDetail_User", "UserDetail").Value; 13 } 14 set 15 { 16 ((IEntityWithRelationships)this).RelationshipManager.GetRelatedReference<UserDetail>("MembershipModel.FK_UserDetail_User", "UserDetail").Value = value; 17 } 18 } 19 [BrowsableAttribute(false)] 20 [DataMemberAttribute()] 21 public EntityReference<UserDetail> UserDetailReference 22 { 23 get 24 { 25 return ((IEntityWithRelationships)this).RelationshipManager.GetRelatedReference<UserDetail>("MembershipModel.FK_UserDetail_User", "UserDetail"); 26 } 27 set 28 { 29 if ((value != null)) 30 { 31 ((IEntityWithRelationships)this).RelationshipManager.InitializeRelatedReference<UserDetail>("MembershipModel.FK_UserDetail_User", "UserDetail", value); 32 } 33 } 34 } 35 #endregion 36 } 37 38 [EdmEntityTypeAttribute(NamespaceName="MembershipModel", Name="UserDetail")] 39 [Serializable()] 40 [DataContractAttribute(IsReference=true)] 41 public partial class UserDetail : EntityObject 42 { 43 #region 導航屬性 44 [XmlIgnoreAttribute()] 45 [SoapIgnoreAttribute()] 46 [DataMemberAttribute()] 47 [EdmRelationshipNavigationPropertyAttribute("MembershipModel", "FK_UserDetail_User", "User")] 48 public User User 49 { 50 get 51 { 52 return ((IEntityWithRelationships)this).RelationshipManager.GetRelatedReference<User>("MembershipModel.FK_UserDetail_User", "User").Value; 53 } 54 set 55 { 56 ((IEntityWithRelationships)this).RelationshipManager.GetRelatedReference<User>("MembershipModel.FK_UserDetail_User", "User").Value = value; 57 } 58 } 59 /// <summary> 60 /// 沒有元數據文檔可用。 61 /// </summary> 62 [BrowsableAttribute(false)] 63 [DataMemberAttribute()] 64 public EntityReference<User> UserReference 65 { 66 get 67 { 68 return ((IEntityWithRelationships)this).RelationshipManager.GetRelatedReference<User>("MembershipModel.FK_UserDetail_User", "User"); 69 } 70 set 71 { 72 if ((value != null)) 73 { 74 ((IEntityWithRelationships)this).RelationshipManager.InitializeRelatedReference<User>("MembershipModel.FK_UserDetail_User", "User", value); 75 } 76 } 77 } 78 79 #endregion 80 }
可以看到,實體類中除了包括CSDL中定義的導航屬性外,還額外定義了一個*Reference屬性,該屬性是用於實現自動延遲加載的。
二、一對多
Role的Parent和Children是一對多的自關聯關係(並不是所有一對多關係都是自關聯的,只是本文所使用的E-R模型正好是如此的),下面直接給出實體數據模型各部分關鍵內容。
SSDL:
1 <edmx:StorageModels> 2 <Schema> 3 <EntityContainer Name="MembershipModelStoreContainer"> 4 … 5 <AssociationSet Name="FK_Role_Role" Association="MembershipModel.Store.FK_Role_Role"> 6 <End Role="Parent" EntitySet="Role" /> 7 <End Role="Children" EntitySet="Role" /> 8 </AssociationSet> 9 </EntityContainer> 10 <EntityType Name="Role"> 11 … 12 <Property Name="ParentID" Type="uniqueidentifier" /> 13 </EntityType> 14 <Association Name="FK_Role_Role"> 15 <End Role="Parent" Type="MembershipModel.Store.Role" Multiplicity="0..1" /> 16 <End Role="Children" Type="MembershipModel.Store.Role" Multiplicity="*" /> 17 <ReferentialConstraint> 18 <Principal Role="Parent"> 19 <PropertyRef Name="ID" /> 20 </Principal> 21 <Dependent Role="Children"> 22 <PropertyRef Name="ParentID" /> 23 </Dependent> 24 </ReferentialConstraint> 25 </Association> 26 </Schema> 27 </edmx:StorageModels>
CSDL:
1 <edmx:ConceptualModels> 2 <Schema> 3 <EntityContainer Name="Membership" annotation:LazyLoadingEnabled="true"> 4 … 5 <AssociationSet Name="FK_Role_Role" Association="MembershipModel.FK_Role_Role"> 6 <End Role="Parent" EntitySet="Roles" /> 7 <End Role="Children" EntitySet="Roles" /> 8 </AssociationSet> 9 <EntityType Name="Role"> 10 … 11 <Property Name="ParentID" Type="Guid" /> 12 <NavigationProperty Name="Parent" Relationship="MembershipModel.FK_Role_Role" FromRole="Parent" ToRole="Children" /> 13 <NavigationProperty Name="Children" Relationship="MembershipModel.FK_Role_Role" FromRole="Children" ToRole="Parent" /> 14 … 15 </EntityType> 16 <Association Name="FK_Role_Role"> 17 <End Role="Parent" Type="MembershipModel.Role" Multiplicity="0..1" /> 18 <End Role="Children" Type="MembershipModel.Role" Multiplicity="*" /> 19 <ReferentialConstraint> 20 <Principal Role="Parent"> 21 <PropertyRef Name="ID" /> 22 </Principal> 23 <Dependent Role="Children"> 24 <PropertyRef Name="ParentID" /> 25 </Dependent> 26 </ReferentialConstraint> 27 </Association> 28 </Schema> 29 </edmx:ConceptualModels>
MSL:MSL中不需要對一對一映射作任何定義,只是簡單的定義表與實體、字段與屬性映射關係即可。
關係源元數據:
1 [assembly: EdmRelationshipAttribute("MembershipModel", "FK_Role_Role", "Parent", System.Data.Metadata.Edm.RelationshipMultiplicity.ZeroOrOne, typeof(Apollo.Blog.EF.Chapter3.Edm.Role), "Children", System.Data.Metadata.Edm.RelationshipMultiplicity.Many, typeof(Apollo.Blog.EF.Chapter3.Edm.Role), true)]
實體:
1 public partial class Role : EntityObject 2 { 3 #region 基元屬性 4 … 5 [EdmScalarPropertyAttribute(EntityKeyProperty=false, IsNullable=true)] 6 [DataMemberAttribute()] 7 public Nullable<global::System.Guid> ParentID 8 { 9 get 10 { 11 return _ParentID; 12 } 13 set 14 { 15 OnParentIDChanging(value); 16 ReportPropertyChanging("ParentID"); 17 _ParentID = StructuralObject.SetValidValue(value); 18 ReportPropertyChanged("ParentID"); 19 OnParentIDChanged(); 20 } 21 } 22 private Nullable<global::System.Guid> _ParentID; 23 partial void OnParentIDChanging(Nullable<global::System.Guid> value); 24 partial void OnParentIDChanged(); 25 #endregion 26 27 #region 導航屬性 28 [XmlIgnoreAttribute()] 29 [SoapIgnoreAttribute()] 30 [DataMemberAttribute()] 31 [EdmRelationshipNavigationPropertyAttribute("MembershipModel", "FK_Role_Role", "Children")] 32 public EntityCollection<Role> Parent 33 { 34 get 35 { 36 return ((IEntityWithRelationships)this).RelationshipManager.GetRelatedCollection<Role>("MembershipModel.FK_Role_Role", "Children"); 37 } 38 set 39 { 40 if ((value != null)) 41 { 42 ((IEntityWithRelationships)this).RelationshipManager.InitializeRelatedCollection<Role>("MembershipModel.FK_Role_Role", "Children", value); 43 } 44 } 45 } 46 47 /// <summary> 48 /// 沒有元數據文檔可用。 49 /// </summary> 50 [XmlIgnoreAttribute()] 51 [SoapIgnoreAttribute()] 52 [DataMemberAttribute()] 53 [EdmRelationshipNavigationPropertyAttribute("MembershipModel", "FK_Role_Role", "Parent")] 54 public Role Children 55 { 56 get 57 { 58 return ((IEntityWithRelationships)this).RelationshipManager.GetRelatedReference<Role>("MembershipModel.FK_Role_Role", "Parent").Value; 59 } 60 set 61 { 62 ((IEntityWithRelationships)this).RelationshipManager.GetRelatedReference<Role>("MembershipModel.FK_Role_Role", "Parent").Value = value; 63 } 64 } 65 66 [BrowsableAttribute(false)] 67 [DataMemberAttribute()] 68 public EntityReference<Role> ChildrenReference 69 { 70 get 71 { 72 return ((IEntityWithRelationships)this).RelationshipManager.GetRelatedReference<Role>("MembershipModel.FK_Role_Role", "Parent"); 73 } 74 set 75 { 76 if ((value != null)) 77 { 78 ((IEntityWithRelationships)this).RelationshipManager.InitializeRelatedReference<Role>("MembershipModel.FK_Role_Role", "Parent", value); 79 } 80 } 81 } 82 #endregion 83 }
三、多對多
User和Role是多對多的關係,下面直接給出實體數據模型各部分關鍵內容。
SSDL:
1 <edmx:StorageModels> 2 <Schema> 3 <EntityContainer Name="MembershipModelStoreContainer"> 4 … 5 <EntitySet Name="UserRole" EntityType="MembershipModel.Store.UserRole" store:Type="Tables" Schema="dbo" /> 6 … 7 <AssociationSet Name="FK_UserRole_Role" Association="MembershipModel.Store.FK_UserRole_Role"> 8 <End Role="Role" EntitySet="Role" /> 9 <End Role="UserRole" EntitySet="UserRole" /> 10 </AssociationSet> 11 <AssociationSet Name="FK_UserRole_User" Association="MembershipModel.Store.FK_UserRole_User"> 12 <End Role="User" EntitySet="User" /> 13 <End Role="UserRole" EntitySet="UserRole" /> 14 </AssociationSet> 15 </EntityContainer> 16 … 17 <EntityType Name="UserRole"> 18 <Key> 19 <PropertyRef Name="UserID" /> 20 <PropertyRef Name="RoleID" /> 21 </Key> 22 <Property Name="UserID" Type="uniqueidentifier" Nullable="false" /> 23 <Property Name="RoleID" Type="uniqueidentifier" Nullable="false" /> 24 </EntityType> 25 … 26 <Association Name="FK_UserRole_Role"> 27 <End Role="Role" Type="MembershipModel.Store.Role" Multiplicity="1" /> 28 <End Role="UserRole" Type="MembershipModel.Store.UserRole" Multiplicity="*" /> 29 <ReferentialConstraint> 30 <Principal Role="Role"> 31 <PropertyRef Name="ID" /> 32 </Principal> 33 <Dependent Role="UserRole"> 34 <PropertyRef Name="RoleID" /> 35 </Dependent> 36 </ReferentialConstraint> 37 </Association> 38 <Association Name="FK_UserRole_User"> 39 <End Role="User" Type="MembershipModel.Store.User" Multiplicity="1" /> 40 <End Role="UserRole" Type="MembershipModel.Store.UserRole" Multiplicity="*" /> 41 <ReferentialConstraint> 42 <Principal Role="User"> 43 <PropertyRef Name="ID" /> 44 </Principal> 45 <Dependent Role="UserRole"> 46 <PropertyRef Name="UserID" /> 47 </Dependent> 48 </ReferentialConstraint> 49 </Association> 50 </Schema> 51 </edmx:StorageModels>
CSDL:
1 <edmx:ConceptualModels> 2 <Schema> 3 <EntityContainer Name="Membership" annotation:LazyLoadingEnabled="true"> 4 <EntitySet Name="Roles" EntityType="MembershipModel.Role" /> 5 <EntitySet Name="Users" EntityType="MembershipModel.User" /> 6 … 7 <AssociationSet Name="UserRole" Association="MembershipModel.UserRole"> 8 <End Role="Role" EntitySet="Roles" /> 9 <End Role="User" EntitySet="Users" /> 10 </AssociationSet> 11 </EntityContainer> 12 <EntityType Name="Role"> 13 … 14 <NavigationProperty Name="Users" Relationship="MembershipModel.UserRole" FromRole="Role" ToRole="User" /> 15 </EntityType> 16 <EntityType Name="User"> 17 … 18 <NavigationProperty Name="Roles" Relationship="MembershipModel.UserRole" FromRole="User" ToRole="Role" /> 19 </EntityType> 20 … 21 <Association Name="UserRole"> 22 <End Role="Role" Type="MembershipModel.Role" Multiplicity="*" /> 23 <End Role="User" Type="MembershipModel.User" Multiplicity="*" /> 24 </Association> 25 </Schema> 26 </edmx:ConceptualModels>
MSL部分需要專門爲多對多關係進行映射定義,如下所示:
1 <edmx:Mappings> 2 <Mapping> 3 <EntityContainerMapping StorageEntityContainer="MembershipModelStoreContainer" CdmEntityContainer="Membership"> 4 … 5 <AssociationSetMapping Name="UserRole" TypeName="MembershipModel.UserRole" StoreEntitySet="UserRole"> 6 <EndProperty Name="Role"> 7 <ScalarProperty Name="ID" ColumnName="RoleID" /> 8 </EndProperty> 9 <EndProperty Name="User"> 10 <ScalarProperty Name="ID" ColumnName="UserID" /> 11 </EndProperty> 12 </AssociationSetMapping> 13 </EntityContainerMapping> 14 </Mapping> 15 </edmx:Mappings>
關係源元數據如下所示:
1 [assembly: EdmRelationshipAttribute("MembershipModel", "UserRole", "Role", System.Data.Metadata.Edm.RelationshipMultiplicity.Many, typeof(Apollo.Blog.EF.Chapter3.Edm.Role), "User", System.Data.Metadata.Edm.RelationshipMultiplicity.Many, typeof(Apollo.Blog.EF.Chapter3.Edm.User))]
實體:
1 public partial class Role : EntityObject 2 { 3 … 4 #region 導航屬性 5 … 6 [XmlIgnoreAttribute()] 7 [SoapIgnoreAttribute()] 8 [DataMemberAttribute()] 9 [EdmRelationshipNavigationPropertyAttribute("MembershipModel", "UserRole", "User")] 10 public EntityCollection<User> Users 11 { 12 get 13 { 14 return ((IEntityWithRelationships)this).RelationshipManager.GetRelatedCollection<User>("MembershipModel.UserRole", "User"); 15 } 16 set 17 { 18 if ((value != null)) 19 { 20 ((IEntityWithRelationships)this).RelationshipManager.InitializeRelatedCollection<User>("MembershipModel.UserRole", "User", value); 21 } 22 } 23 } 24 25 #endregion 26 } 27 28 public partial class User : EntityObject 29 { 30 … 31 #region 導航屬性 32 … 33 [XmlIgnoreAttribute()] 34 [SoapIgnoreAttribute()] 35 [DataMemberAttribute()] 36 [EdmRelationshipNavigationPropertyAttribute("MembershipModel", "UserRole", "Role")] 37 public EntityCollection<Role> Roles 38 { 39 get 40 { 41 return ((IEntityWithRelationships)this).RelationshipManager.GetRelatedCollection<Role>("MembershipModel.UserRole", "Role"); 42 } 43 set 44 { 45 if ((value != null)) 46 { 47 ((IEntityWithRelationships)this).RelationshipManager.InitializeRelatedCollection<Role>("MembershipModel.UserRole", "Role", value); 48 } 49 } 50 } 51 52 #endregion 53 }
最後,給出該實體數據模型的簡單測試代碼,從中可以看出該模型使用起來是很方便和諧的:
1 using (var db = new Membership()) 2 { 3 var role = new Role(); 4 role.ID = Guid.NewGuid(); 5 role.Name = "管理員"; 6 7 var role1 = new Role(); 8 role1.ID = Guid.NewGuid(); 9 role1.Name = "業務管理員"; 10 role1.Parent = role; 11 12 var user = new User(); 13 user.ID = Guid.NewGuid(); 14 user.Account = "Apollo"; 15 user.Password = "123456"; 16 user.UserDetail = new UserDetail() { ID = user.ID, Name = "Yilin", Sex = "男", Birthday = DateTime.Now }; 17 user.Roles.Add(role1); 18 db.Users.AddObject(user); 19 20 db.SaveChanges(); 21 }
總結
本文首先給出了實體數據模型內容結構圖,宏觀分析了實體數據模型主要由SSDL、CSDL、MSL、實體和ObjectContext幾部分組成;緊接着通過一個實例逐一分析了這幾部分的詳細內容;最後詳細講解了如何使用Entity Framework技術將一對一、一對多和多對多三種E-R關係映射爲實體數據模型。
下一篇文章將在本文的基礎上,探討如何駕馭實體數據模型的各組成內容,將Entity Framework技術靈活應用到項目開發中