在Entity Framework中使用存储过程(五):如何通过存储过程维护多对多关系?

对于数据库设计来说,多对多(或者一对多)是一种常见的数据关系,比如联系人和地址之间的关系。我们通常采用建立关系表的方式来表示这种关系,比如我们创建一张Contact—_Address(ContactID, AddressID)来存储联系人和地址之间的关系。如果我们最终需要通过存储过程的方式来维护他们之间的关系,该如何做呢?本篇文章给你一个具体的例子来演示如果采用存储过程来建立和删除实体之间的关系。

目录

步骤一、创建数据表
步骤二、创建建立/解除关系的存储过程
步骤三、创建实体数据模型
步骤四、建立关系与存储过程的映射
步骤五、编写建立Contact/Address关联的程序

 步骤一、创建数据表

我们就采用上面提到过的联系人/地址关联的场景,现在我们通过下面的SQL来创建三张表。Contact和Address分别用于存储联系人和地址记录,两者之间的关系存储在Contact_Address表中。

Address:

   1: CREATE TABLE [Address]
   2: (
   3:     [AddressID]     [INT]          PRIMARY KEY,
   4:     [StreetAddress] [NVARCHAR](50) NOT NULL,
   5:     [City]          [NVARCHAR](50) NOT NULL
   6: )

Contact:

   1: CREATE TABLE [Contact]
   2: (
   3:     [ContactID]        [INT]            PRIMARY KEY,
   4:     [LastName]        [NVARCHAR](50)    NOT NULL,
   5:     [FirstName]        [NVARCHAR](50)    NOT NULL
   6: )

Contact_Address:

   1: CREATE TABLE [Contact_Address]
   2: (
   3:     [ContactID] [INT]  NOT NULL REFERENCES [Contact]([ContactID]),
   4:     [AddressID] [INT]  NOT NULL REFERENCES [Address]([AddressID]),
   5:     PRIMARY KEY([ContactID],[AddressID])
   6: )

上面创建的三张数据表在数据库中具有如下图所示的关系:

image

步骤二、创建建立/解除关系的存储过程

我们需要演示的是如何通过存储过程来建立和接触Contact和Address之间的关系,也就是通过存储过程来维护Contact_Address这张表的记录。我们只需创建两个对应的存储过程:AddAddress和DeleteAddress。和基于实体数据更新同时需要CUD三个存储过程不同,这里只需CD两个存储过程。

AddAddress:

   1: CREATE PROCEDURE [AddAddress] 
   2: ( @ContactID [INT],
   3:   @AddressID [INT]
   4: )
   5: AS
   6: BEGIN
   7:     INSERT Contact_Address(ContactID, AddressID)
   8:     VALUES(@ContactID, @AddressID);
   9: END

DeleteAddress:

   1: CREATE PROCEDURE [DeleteAddress] 
   2:     @ContactID int,
   3:     @AddressID int
   4: AS
   5:  
   6: BEGIN
   7:     DELETE Contact_Address
   8:     WHERE ContactID = @ContactID 
   9:       AND AddressID = @AddressID
  10: END

步骤三、创建实体数据模型

然后我们只需要按照VS提供的实体数据模型创建向导,通过选择上面创建的三张表和两个存储过程建立如下一个.edmx模型。我们可以看到,虽然我们选择了三张表,EF能够解析出Contact_Address为关系表,所以最终生成出来的就是我们希望的具有多对多(如果一个联系人只有一个地址,你可以将关系更新成一对多)。需要注意的是,只有当关系表仅仅包括外键的情况下才会被EF认为是关系表。如果Contact_Address具有额外的字段,在建立模型的时候仍然被认作是实体表。

image

步骤四、建立关系与存储过程的映射

由于在建立模型的时候我们仅仅是选择了我们创建的两个存储过程,所以对于.edmx模型的元数据(概念模型、存储模型和C/S映射)来说,这两个存储过程仅仅体现在存储模型中。现在我们需要最终调用它们来建立或者删除Contact和Address之间的关系,我们肯定需要在C/S映射中定义实体关系(概念模型)和这两个存储过程对应的Function(存储模型)之间的关联。

你肯定知道我们可以通过EF提供的设计器为每一个实体类型定义CUD存储过程,以及实体属性和存储过程参数之间的映射关系。但是现在我们要完成的却实关系(Association)与存储过程之间的映射。很遗憾,这项工作没有得到EF设计器可视化的支持,我们不得不通过手工修改.edmx模型的XML来完成。

现在你需要通过XML Editor打开.edmx文件,定位到C/S映射节点(<edmx:Mappings>),找到如下一段表示Contact/Address关系映射的名称为AssociationSetMapping的XML元素。

   1: <AssociationSetMapping Name="Contact_Address" TypeName="ContactModel.Contact_Address" StoreEntitySet="Contact_Address">
   2:   <EndProperty Name="Address">
   3:     <ScalarProperty Name="AddressID" ColumnName="AddressID" />
   4:   </EndProperty>
   5:   <EndProperty Name="Contact">
   6:     <ScalarProperty Name="ContactID" ColumnName="ContactID" />
   7:   </EndProperty>  
   8: </AssociationSetMapping>

然后,在AssociationSetMapping节点中添加ModificationFunctionMapping节点,在InsertFunction和DeleteFunction中分别定义我们创建的两个存储过程的映射关系。这个AssociationSetMapping节点如下所示,FunctionName中的命名空间根据你具体的定义作相应修改。

   1: <AssociationSetMapping Name="Contact_Address" TypeName="ContactModel.Contact_Address" StoreEntitySet="Contact_Address">
   2:   <EndProperty Name="Address">
   3:     <ScalarProperty Name="AddressID" ColumnName="AddressID" />
   4:   </EndProperty>
   5:   <EndProperty Name="Contact">
   6:     <ScalarProperty Name="ContactID" ColumnName="ContactID" />
   7:   </EndProperty>
   8:   <ModificationFunctionMapping>
   9:     <InsertFunction FunctionName="ContactModel.Store.AddAddress">
  10:       <EndProperty Name="Address">
  11:         <ScalarProperty Name="AddressID" ParameterName="AddressID"/>
  12:       </EndProperty>
  13:       <EndProperty Name="Contact">
  14:         <ScalarProperty Name="ContactID" ParameterName="ContactID"/>
  15:       </EndProperty>
  16:     </InsertFunction>
  17:     <DeleteFunction FunctionName="ContactModel.Store.DeleteAddress">
  18:       <EndProperty Name="Address">
  19:         <ScalarProperty Name="AddressID" ParameterName="AddressID"/>
  20:       </EndProperty>
  21:       <EndProperty Name="Contact">
  22:         <ScalarProperty Name="ContactID" ParameterName="ContactID"/>
  23:       </EndProperty>
  24:     </DeleteFunction>
  25:   </ModificationFunctionMapping>
  26: </AssociationSetMapping>

步骤五、编写建立Contact/Address关联的程序

现在我们编写如下的程序,先后创建2个地址和3个联系人,并分别建立它们之间的关系后,通过调用ObjectContext的SaveChanges方法提交到数据库中。

   1: Address address1    = Address.CreateAddress(1, "Jinji Hu Rd #328 E101", "Su Zhou");
   2: Address address2    = Address.CreateAddress(2, "XingHu Street #328 Blk 11", "Su Zhou");
   3: Contact zhangSan    = Contact.CreateContact(1, "Zhang", "San");
   4: Contact liSi        = Contact.CreateContact(2, "Li", "Si");
   5: Contact wangWu      = Contact.CreateContact(3, "Wang", "Wu");
   6:  
   7: zhangSan.Addresses.Add(address1);
   8: liSi.Addresses.Add(address1);
   9: wangWu.Addresses.Add(address2);
  10:  
  11: using (ContactInformationEntities context = new ContactInformationEntities())
  12: {
  13:     context.Contacts.AddObject(zhangSan);
  14:     context.Contacts.AddObject(liSi);
  15:     context.Contacts.AddObject(wangWu);
  16:     context.SaveChanges();
  17: }

查看Contact_Address这张表的数据,你会发现相应的关系已经被正确地保存。

image

在Entity Framework中使用存储过程(一):实现存储过程的自动映射
在Entity Framework中使用存储过程(二):具有继承关系实体的存储过程如何定义?
在Entity Framework中使用存储过程(三):逻辑删除的实现与自增长列值返回
在Entity Framework中使用存储过程(四):如何为Delete存储过程参数赋上Current值?
在Entity Framework中使用存储过程(五):如何通过存储过程维护多对多关系?

posted @ 2011-03-16 16:41 Artech 阅读(...) 评论(...) 编辑 收藏