在 实体数据模型 (EDM) 中,以概念架构定义语言 (CSDL) 定义关联。关联的实现将 CSDL 中的定义映射到存储元数据,并从定义它们所关联的实体的同一架构中生成关联。
本主题以及主题使用关联的应用程序代码 (EDM) 通过考察简单的端对端示例,介绍如何将关联映射到存储和在代码中初始化以及应用程序如何导航关联数据。
客户、订单、订单行关联
在业务 (LOB) 应用程序中,表示客户和订单的实体在逻辑上是相关的。客户采购的产品由订单表示。客户和订单通过名为 Order_Customer 的 Association 元素相关。
关联中的每个实体称为一个 End。对于此关联,关联中表示客户的 End 可以与许多订单相关,但表示订单的 End 只能与一个客户相关。在架构语法中,这一点通过使用 Multiplicity 属性进行定义。此关联中名为 Customers 的 End 具有 Multiplicity="1",而名为 Orders 的 End 具有 Multiplicity="*"。这是一对多关联的示例。有关关联类型的更多信息,请参见关联 (EDM)。
使用 Association 架构元素定义 Order_Customer 关联后,该关联将作为 AssociationSet 与此示例中所使用的实体一起包含在 EntityContainer 中。有关关联集和实体容器的更多信息,请参见 EntityContainer 元素 (CSDL)。
下面的架构示例定义表示 Customers、Orders、OrderLines 的实体及两个关联:Order_Customer 和 OrderLine_Order。
<?xml version="1.0" encoding="utf-8"?>
      <Schema Namespace="OrderInfoModel" Alias="Self" 
              xmlns="https://schemas.microsoft.com/ado/2006/04/edm">
        <EntityContainer Name="OrderInfoEntities">
          <EntitySet Name="Customers" 
                     EntityType="OrderInfoModel.Customers" />
          <EntitySet Name="OrderLines"
                     EntityType="OrderInfoModel.OrderLines" />
          <EntitySet Name="Orders" 
                     EntityType="OrderInfoModel.Orders" />
          <AssociationSet Name="Order_Customer" 
                          Association="OrderInfoModel.Order_Customer">
            <End Role="Customers" EntitySet="Customers" />
            <End Role="Orders" EntitySet="Orders" />
          </AssociationSet>
          <AssociationSet Name="OrderLine_Order" 
                     Association="OrderInfoModel.OrderLine_Order">
            <End Role="Orders" EntitySet="Orders" />
            <End Role="OrderLines" EntitySet="OrderLines" />
          </AssociationSet>
        </EntityContainer>
        <EntityType Name="Customers">
          <Key>
            <PropertyRef Name="CustomerId" />
          </Key>
          <Property Name="CustomerId" Type="Guid" Nullable="false" />
          <Property Name="Name" Type="String" Nullable="false" 
                    MaxLength="50" Unicode="true" FixedLength="false" />
          <Property Name="Address" Type="String" Nullable="false" 
                    MaxLength="50" Unicode="true" FixedLength="false" />
          <Property Name="City" Type="String" Nullable="false" 
                    MaxLength="50" Unicode="true" FixedLength="false" />
          <Property Name="Phone" Type="String" Nullable="false" 
                    MaxLength="50" Unicode="true" FixedLength="false" />
          <Property Name="ZipCode" Type="Int32" Nullable="false" />
          <NavigationProperty Name="Orders" 
                          Relationship="OrderInfoModel.Order_Customer" 
                          FromRole="Customers" ToRole="Orders" />
        </EntityType>
        <EntityType Name="OrderLines">
          <Key>
            <PropertyRef Name="OrderLineId" />
          </Key>
          <Property Name="OrderLineId" Type="Guid" Nullable="false" />
          <Property Name="ProductName" Type="String" Nullable="false" 
                    MaxLength="50" Unicode="true" FixedLength="false" />
          <Property Name="Quantity" Type="Int32" Nullable="false" />
          <Property Name="UnitPrice" Type="Decimal" Nullable="false" 
                    Precision="19" Scale="4" />
          <Property Name="ExtendedPrice" Type="Decimal" 
                    Nullable="false" Precision="19" Scale="4" />
          <NavigationProperty Name="Orders" 
                          Relationship="OrderInfoModel.OrderLine_Order" 
                          FromRole="OrderLines" ToRole="Orders" />
        </EntityType>
        <EntityType Name="Orders">
          <Key>
            <PropertyRef Name="OrderId" />
          </Key>
          <Property Name="OrderId" Type="String" Nullable="false" 
                    MaxLength="50" Unicode="true" FixedLength="false" />
          <Property Name="TotalAmount" 
                    Type="Decimal" Precision="19" Scale="4" />
          <Property Name="Tax" Type="Decimal" 
                    Precision="19" Scale="4" />
          <Property Name="ShippingAddress" Type="String" 
                    MaxLength="50" Unicode="true" FixedLength="false" />
          <NavigationProperty Name="Customers" 
                    Relationship="OrderInfoModel.Order_Customer" 
                    FromRole="Orders" ToRole="Customers" />
          <NavigationProperty Name="OrderLines" 
                    Relationship="OrderInfoModel.OrderLine_Order" 
                    FromRole="Orders" ToRole="OrderLines" />
        </EntityType>
        <Association Name="Order_Customer">
          <End Role="Customers" 
               Type="OrderInfoModel.Customers" Multiplicity="1" />
          <End Role="Orders" 
               Type="OrderInfoModel.Orders" Multiplicity="*" />
        </Association>
        <Association Name="OrderLine_Order">
          <End Role="Orders" 
               Type="OrderInfoModel.Orders" Multiplicity="1" />
          <End Role="OrderLines" 
               Type="OrderInfoModel.OrderLines" Multiplicity="*" />
        </Association>
      </Schema>
对于 Order_Customer 关联,已在 Customers 实体上实现 NavigationProperty 以为该关联提供方便的导航和初始化。Orders 实体也指定了用于初始化和导航它所包含的 OrderLines 的 NavigationProperty。有关 NavigationProperty 定义的更多信息,请参见导航属性 (EDM)。
关联映射和元数据
在此示例中,映射一对多关联(如 Order_Customer 关联)的 Customers 和 Orders 是通过以下方式实现的:将此关联映射到数据库中 Orders 表与 Customers 表之间的一个外键关系。通过此方法,Orders 表将包含 Customers 外键的多个实例,以表示与每个客户关联的订单。
存储架构
以下存储架构定义语言 (SSDL) 架构是表示 Customers 表、Orders 表和 OrderLines 表的存储元数据。SSDL 架构使用与数据库中的表相对应的 EntityType 元素声明这些表。将根据数据库管理系统的数据类型类型化实体的属性。例如,Customers 实体的 Name 属性将类型化为 nvarchar(而非在 CSDL 架构中使用的 String 类型)。
<?xml version="1.0" encoding="utf-8"?>
      <Schema Namespace="OrderInfoModel.Store" 
              Alias="Self" Provider="System.Data.SqlClient" 
              ProviderManifestToken="2005" 
xmlns:store="https://schemas.microsoft.com/ado/2007/12/edm/EntityStoreSchemaGenerator" 
              xmlns="https://schemas.microsoft.com/ado/2006/04/edm/ssdl">
        <EntityContainer Name="OrderInfoModelStoreContainer">
          <EntitySet Name="Customers" 
                     EntityType="OrderInfoModel.Store.Customers" 
                     store:Type="Tables" Schema="dbo" />
          <EntitySet Name="OrderLines" 
                     EntityType="OrderInfoModel.Store.OrderLines" 
                     store:Type="Tables" Schema="dbo" />
          <EntitySet Name="Orders" 
                     EntityType="OrderInfoModel.Store.Orders" 
                     store:Type="Tables" Schema="dbo" />
          <AssociationSet Name="Order_Customer" 
                 Association="OrderInfoModel.Store.Order_Customer">
            <End Role="Customers" EntitySet="Customers" />
            <End Role="Orders" EntitySet="Orders" />
          </AssociationSet>
          <AssociationSet Name="OrderLine_Order" 
                 Association="OrderInfoModel.Store.OrderLine_Order">
            <End Role="Orders" EntitySet="Orders" />
            <End Role="OrderLines" EntitySet="OrderLines" />
          </AssociationSet>
        </EntityContainer>
        <EntityType Name="Customers">
          <Key>
            <PropertyRef Name="CustomerId" />
          </Key>
          <Property Name="CustomerId" 
                    Type="uniqueidentifier" Nullable="false" />
          <Property Name="Name" 
                    Type="nvarchar" Nullable="false" MaxLength="50" />
          <Property Name="Address" 
                    Type="nvarchar" Nullable="false" MaxLength="50" />
          <Property Name="City" 
                    Type="nvarchar" Nullable="false" MaxLength="50" />
          <Property Name="Phone" 
                    Type="nvarchar" Nullable="false" MaxLength="50" />
          <Property Name="ZipCode" 
                    Type="int" Nullable="false" />
        </EntityType>
        <EntityType Name="OrderLines">
          <Key>
            <PropertyRef Name="OrderLineId" />
          </Key>
          <Property Name="OrderLineId" 
                    Type="uniqueidentifier" Nullable="false" />
          <Property Name="OrderId" 
                    Type="nvarchar" Nullable="false" MaxLength="50" />
          <Property Name="ProductName"
                    Type="nvarchar" Nullable="false" MaxLength="50" />
          <Property Name="Quantity" Type="int" Nullable="false" />
          <Property Name="UnitPrice" 
                    Type="money" Nullable="false" />
          <Property Name="ExtendedPrice" 
                    Type="money" Nullable="false" />
        </EntityType>
        <EntityType Name="Orders">
          <Key>
            <PropertyRef Name="OrderId" />
          </Key>
          <Property Name="OrderId" 
                    Type="nvarchar" Nullable="false" MaxLength="50" />
          <Property Name="Customer" 
                    Type="uniqueidentifier" Nullable="false" />
          <Property Name="TotalAmount" Type="money" />
          <Property Name="Tax" 
                    Type="money" />
          <Property Name="ShippingAddress" 
                    Type="nvarchar" MaxLength="50" />
        </EntityType>
        <Association Name="Order_Customer">
          <End Role="Customers"
               Type="OrderInfoModel.Store.Customers" Multiplicity="1" />
          <End Role="Orders" 
               Type="OrderInfoModel.Store.Orders" Multiplicity="*" />
          <ReferentialConstraint>
            <Principal Role="Customers">
              <PropertyRef Name="CustomerId" />
            </Principal>
            <Dependent Role="Orders">
              <PropertyRef Name="Customer" />
            </Dependent>
          </ReferentialConstraint>
        </Association>
        <Association Name="OrderLine_Order">
          <End Role="Orders" 
               Type="OrderInfoModel.Store.Orders" Multiplicity="1" />
          <End Role="OrderLines" 
               Type="OrderInfoModel.Store.OrderLines" Multiplicity="*" />
          <ReferentialConstraint>
            <Principal Role="Orders">
              <PropertyRef Name="OrderId" />
            </Principal>
            <Dependent Role="OrderLines">
              <PropertyRef Name="OrderId" />
            </Dependent>
          </ReferentialConstraint>
        </Association>
      </Schema>
当在数据库中实现 Customers 和 Orders 实体时,这一存储架构(以 SSDL 编写)指定这些实体的数据类型。在 CSDL 架构中属于 Type="Guid" 的键属性在数据库中具有 Type="uniqueidentifier",必须使用存储架构中的数据库类型来指定这些属性。
在 CSDL 中属于 String 类型的属性映射为存储中的 nvarchar 类型。
存储架构中的关联规范与 CSDL 架构中的关联规范相同。像在 CSDL 架构中一样,关联的 End 元素也是使用 Role、Type 和 Multiplicity 属性声明的。End 和 Role 的指定与 CSDL 中的相同。
ReferentialConstraint 属性指示关联依赖于数据库结构。ReferentialConstraint 指定 Principle Role 和 Dependent Role 以及 PropertyRef 元素。PropertyRef 属性 (Attribute) 指定 End 实体的表示数据库表的主键和外键列的属性 (Property),这些列与 Association 中的实体相对应。Principle Role 的 PropertyRef 属性指定包含主键的列。例如,在 Order_Customer 关联中,Principle Role 的 PropertyRef 是 CustomerId 属性。SSDL 架构中的这一属性表示 Customers 表的 CustomerId 列,此列将分配给 Orders 表中的外键列 Customer。该外键由 Dependent Role 的 PropertyRef 表示。
映射规范
映射关联类似于映射实体。
以下映射规范语言 (MSL) 段显示名为 Order_Customer 的 AssociationSetMapping。在此示例中,在 CSDL 架构中指定的 OrderInfoModel.Order_Customer 关联映射到表示数据库的 SSDL 架构中的 Orders 表。
此关联的 Customers 端的 PropertyRef 显式将 Customers 实体的 CustomerId 属性映射到 Orders 表的 Customer 列;此列包含表示数据库中 Orders 表和 Customers 表之间关系的外键。
以下示例显示从中提取上一段的完整 MSL 架构。此映射同时包含此示例所需的 EntitySetMapping 和 AssociationSetMapping 元素。
<?xml version="1.0" encoding="utf-8"?>
      <Mapping Space="C-S" 
   xmlns="urn:schemas-microsoft-com:windows:storage:mapping:CS">
        <EntityContainerMapping 
          StorageEntityContainer="OrderInfoModelStoreContainer" 
          CdmEntityContainer="OrderInfoEntities">
          <EntitySetMapping Name="Customers">
            <EntityTypeMapping 
              TypeName="IsTypeOf(OrderInfoModel.Customers)">
              <MappingFragment StoreEntitySet="Customers">
                <ScalarProperty Name="CustomerId" 
                                ColumnName="CustomerId" />
                <ScalarProperty Name="Name" ColumnName="Name" />
                <ScalarProperty Name="Address" ColumnName="Address" />
                <ScalarProperty Name="City" ColumnName="City" />
                <ScalarProperty Name="Phone" ColumnName="Phone" />
                <ScalarProperty Name="ZipCode" ColumnName="ZipCode" />
              </MappingFragment>
            </EntityTypeMapping>
          </EntitySetMapping>
          <EntitySetMapping Name="OrderLines">
            <EntityTypeMapping 
              TypeName="IsTypeOf(OrderInfoModel.OrderLines)">
              <MappingFragment StoreEntitySet="OrderLines">
                <ScalarProperty Name="OrderLineId" 
                                ColumnName="OrderLineId" />
                <ScalarProperty Name="ProductName" 
                                ColumnName="ProductName" />
                <ScalarProperty Name="Quantity" 
                                ColumnName="Quantity" />
                <ScalarProperty Name="UnitPrice" 
                                ColumnName="UnitPrice" />
                <ScalarProperty Name="ExtendedPrice" 
                                ColumnName="ExtendedPrice" />
              </MappingFragment>
            </EntityTypeMapping>
          </EntitySetMapping>
          <EntitySetMapping Name="Orders">
            <EntityTypeMapping 
              TypeName="IsTypeOf(OrderInfoModel.Orders)">
              <MappingFragment StoreEntitySet="Orders">
                <ScalarProperty Name="OrderId" ColumnName="OrderId" />
                <ScalarProperty Name="TotalAmount" 
                                ColumnName="TotalAmount" />
                <ScalarProperty Name="Tax" ColumnName="Tax" />
                <ScalarProperty Name="ShippingAddress" 
                                ColumnName="ShippingAddress" />
              </MappingFragment>
            </EntityTypeMapping>
          </EntitySetMapping>
          <AssociationSetMapping Name="Order_Customer" 
                               TypeName="OrderInfoModel.Order_Customer" 
                               StoreEntitySet="Orders">
            <EndProperty Name="Customers">
              <ScalarProperty Name="CustomerId" 
                              ColumnName="Customer" />
            </EndProperty>
            <EndProperty Name="Orders">
              <ScalarProperty Name="OrderId" ColumnName="OrderId" />
            </EndProperty>
          </AssociationSetMapping>
          <AssociationSetMapping Name="OrderLine_Order" 
                              TypeName="OrderInfoModel.OrderLine_Order" 
                              StoreEntitySet="OrderLines">
            <EndProperty Name="Orders">
              <ScalarProperty Name="OrderId" 
                              ColumnName="OrderId" />
            </EndProperty>
            <EndProperty Name="OrderLines">
              <ScalarProperty Name="OrderLineId" 
                              ColumnName="OrderLineId" />
            </EndProperty>
          </AssociationSetMapping>
        </EntityContainerMapping>
      </Mapping>
本主题中所示的代码段包含完整的架构,可在 OrderInfo 命名空间中生成编程对象模型并将其映射到存储。有关使用此模型的代码的示例,请参见使用关联的应用程序代码 (EDM)。
分部类方法
在 EDM 中,可以在分部类中实现帮助器方法以改善应用程序的功能。以下示例是 Orders 类的帮助器方法。此帮助器方法汇总订单的 OrderLines 的 ExtendedPrice 金额,以计算 Order 的 TotalAmount。有关更多信息,请参见 帮助器方法 (EDM)。
using System;
using System.Data;
namespace OrderInfoModel
{
    public partial class Orders :
                       global::System.Data.Objects.DataClasses.EntityObject
    {
        public decimal ComputeOrder()
        {
            this.TotalAmount = 0;
            foreach (OrderLines orderLine in this.OrderLines)
            {
                orderLine.ExtendedPrice =
                        orderLine.Quantity * orderLine.UnitPrice;
                this.TotalAmount = this.TotalAmount +
                                    orderLine.ExtendedPrice;
            }
           
            this.Tax = Decimal.Round(((decimal)this.TotalAmount *
                                                 (decimal) .08), 2);
            this.TotalAmount = this.TotalAmount + this.Tax;
            return (decimal)this.TotalAmount;
        }
    }
}
实现数据库
可以使用以下脚本来创建此示例的数据库。若要使用 SQL Server Management Studio 创建 OrderInfo 数据库,请执行以下操作:
- 在“文件”菜单上,指向“新建”,然后单击“数据库引擎查询”。 
- 在“连接到数据库引擎”对话框中,键入 localhost 或 SQL Server 实例的名称,然后单击“连接”。 
- 将以下 Transact-SQL 脚本粘贴到查询窗口中,然后单击“执行”。 
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
USE [master]
GO
IF EXISTS (SELECT * FROM sys.databases 
WHERE name = 'OrderInfo')
DROP DATABASE OrderInfo;
GO
-- Create the database.
CREATE DATABASE OrderInfo;
GO
USE OrderInfo;
GO
IF NOT EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[Customers]') AND type in (N'U'))
BEGIN
CREATE TABLE [Customers](
    [CustomerId] [uniqueidentifier] NOT NULL,
    [Name] [nvarchar](50) NOT NULL,
     [Address] [nvarchar](50) NOT NULL,
     [City] [nvarchar](50) NOT NULL,
     [Phone] [nvarchar](50) NOT NULL,
     [ZipCode] [int] NOT NULL,
 CONSTRAINT [PK_Customers] PRIMARY KEY CLUSTERED 
(
[CustomerId] ASC
)WITH (PAD_INDEX  = OFF, IGNORE_DUP_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY]
END
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
IF NOT EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[Orders]') AND type in (N'U'))
BEGIN
CREATE TABLE [Orders](
    [OrderId] [nvarchar](50) NOT NULL,
    [Customer] [uniqueidentifier] NOT NULL,
    [TotalAmount] [money] NULL,
    [Tax] [money] NULL,
    [ShippingAddress] [nvarchar](50) NULL,
 CONSTRAINT [PK_Orders] PRIMARY KEY CLUSTERED 
(
     [OrderId] ASC
)WITH (PAD_INDEX  = OFF, IGNORE_DUP_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY]
END
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
IF NOT EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[OrderLines]') AND type in (N'U'))
BEGIN
CREATE TABLE [OrderLines](
    [OrderLineId] [uniqueidentifier] NOT NULL,
    [OrderId] [nvarchar](50) NOT NULL,
    [ProductName] [nvarchar](50) NOT NULL,
    [Quantity] [int] NOT NULL,
    [UnitPrice] [money] NOT NULL,
    [ExtendedPrice] [money] NOT NULL,
 CONSTRAINT [PK_OrderLines] PRIMARY KEY CLUSTERED 
(
     [OrderLineId] ASC
)WITH (PAD_INDEX  = OFF, IGNORE_DUP_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY]
END
GO
IF NOT EXISTS (SELECT * FROM sys.foreign_keys WHERE object_id = OBJECT_ID(N'[Order_Customer]') AND parent_object_id = OBJECT_ID(N'[Orders]'))
ALTER TABLE [Orders]  WITH CHECK ADD  CONSTRAINT [Order_Customer] FOREIGN KEY([Customer])
REFERENCES [Customers] ([CustomerId])
GO
ALTER TABLE [Orders] CHECK CONSTRAINT [Order_Customer]
GO
IF NOT EXISTS (SELECT * FROM sys.foreign_keys WHERE object_id = OBJECT_ID(N'[OrderLine_Order]') AND parent_object_id = OBJECT_ID(N'[OrderLines]'))
ALTER TABLE [OrderLines]  WITH CHECK ADD  CONSTRAINT [OrderLine_Order] FOREIGN KEY([OrderId])
REFERENCES [Orders] ([OrderId])
GO
ALTER TABLE [OrderLines] CHECK CONSTRAINT [OrderLine_Order]
另请参见
概念
关联 (EDM)
导航属性 (EDM)
实现实体 (EDM)