Entity Framework技術系列之3:對象-關係映射

前言

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技術靈活應用到項目開發中

發佈了7 篇原創文章 · 獲贊 8 · 訪問量 8萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章