Поделиться через


Реализация ассоциаций (модель EDM)

В модели Entity Data Model (EDM) ассоциации определяются на языке CSDL. При реализации ассоциаций выполняется сопоставление определений на языке CSDL с метаданными хранилища и создаются ассоциации из той же схемы, в которой определены связанные с ними сущности.

Сопоставление ассоциаций с хранилищем и инициализация в коде, а также навигация приложения по данным ассоциаций рассматриваются в этом разделе и разделе Код приложения с использованием ассоциаций (модель EDM) на простом сквозном примере.

Ассоциация клиента, заказа, линии заказов

В бизнес-приложениях сущности, представляющие клиентов и заказы, логически связаны. Продукты, приобретаемые клиентами, представлены заказами. Клиенты и заказы связываются с помощью элемента Association с именем Order_Customer.

Каждая сущность в ассоциации называется End. В этой ассоциации элемент End, представляющий клиента, может относиться ко многим заказам, но элемент End, представляющий заказ, может относиться только к одному клиенту. В синтаксисе схемы это определяется атрибутом Multiplicity. Элемент End этой ассоциации, называемый Customers, имеет свойство Multiplicity="1", а элемент End, называемый Orders, имеет свойство Multiplicity="*". Это пример ассоциации «один ко многим». Дополнительные сведения о типах ассоциаций см. в разделе Элемент Association (модель EDM).

После определения ассоциации Order_Customer с использованием элементов схемы Association эта ассоциация включается в качестве 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 реализовано свойство NavigationProperty применительно к сущности Customers для предоставления этой ассоциации простых средств навигации и инициализации. Сущность Orders задает также свойство NavigationProperty, которое используется для инициализации и навигации по содержащейся в ней сущности OrderLines. Дополнительные сведения об определении свойства NavigationProperty см. в разделе Свойства навигации (модель EDM).

Сопоставление ассоциаций и метаданные

Сопоставление элементов Customers и Orders ассоциаций типа один ко многим (таких как Order_Customer) в этом примере осуществляется путем сопоставления ассоциации с отношением внешнего ключа между таблицами Orders и Customers в базе данных. При таком подходе предусмотрено хранение нескольких экземпляров внешнего ключа Customers в таблице Orders для представления заказов, связанных с каждым клиентом.

Схема хранения

Следующая схема SSDL является метаданными хранилища и представляет таблицы Customers, Orders и OrderLines. В SSDL-схеме эти таблицы объявлены с использованием элементов EntityType, соответствующих таблицам в базе данных. Свойства сущностей типизированы в соответствии с типами данных системы управления базой данных. Например, свойство Name сущности Customers имеет тип nvarchar, а не String, как в CSDL-схеме.

<?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>

Эта схема хранения, написанная на языке SSDL, задает типы данных для сущностей Customers и Orders в том виде, в каком они реализованы в базе данных. Свойства Key, которые имели значения Type="Guid" в CSDL-схеме, имеют значения Type="uniqueidentifier" в базе данных и должны быть заданы с использованием типов базы данных в схеме хранения.

Свойства, которые относились к типам String на языке CSDL, сопоставляются с типами nvarchar в схеме хранения.

Спецификации ассоциаций одинаковы как в схеме хранения, так и в CSDL-схеме. Как и в CSDL-схеме, элементы End ассоциации объявляются с атрибутами Role, Type и Multiplicity. Присваивания End и Role являются такими же, как и на языке CSDL.

Атрибут ReferentialConstraint указывает, что ассоциации зависят от структур базы данных. Ограничение ReferentialConstraint задает элементы Principle Role и Dependent Role, а также PropertyRef. Атрибуты PropertyRef задают свойства сущностей End, которые представляют столбцы первичного ключа и внешнего ключа таблиц базы данных, соответствующих сущностям в ассоциации Association. Атрибут PropertyRef объекта Principle Role задает столбец, который хранит первичный ключ. Например, в ассоциации Order_Customer свойство PropertyRef объекта Principle Role является свойством CustomerId. В SSDL-схеме это свойство представляет столбец CustomerId таблицы Customers, который будет присвоен столбцу внешнего ключа Customer в таблице Orders. Внешний ключ представлен атрибутом PropertyRef объекта Dependent Role.

Спецификация сопоставления

Сопоставление ассоциаций напоминает сопоставление сущностей.

Следующий фрагмент сопоставления на языке MSL показывает сопоставление AssociationSetMapping, именуемое Order_Customer. В этом примере ассоциация OrderInfoModel.Order_Customer, заданная в CSDL-схеме, сопоставляется с таблицей Orders в SSDL-схеме, представляющей базу данных.

Свойство PropertyRef элемента Customers ассоциации явно сопоставляет свойство CustomerId сущности Customers со столбцом Customer таблицы Orders. Этот столбец содержит внешний ключ, представляющий отношение между таблицами 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. Вспомогательный метод складывает суммы ExtendedPrice для строк OrderLines заказа, чтобы вычислить общую сумму TotalAmount заказа Order. Дополнительные сведения см. в разделе Вспомогательные методы (модель 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;

        }
    }
}

Реализация базы данных

Следующий сценарий можно использовать для создания базы данных, используемой в этом примере. Чтобы создать базу данных OrderInfo в среде SQL Server Management Studio, сделайте следующее.

  1. В меню «Файл» укажите пункт «Создать» и выберите пункт «Запрос к ядру СУБД».

  2. В диалоговом окне «Подключиться к компоненту Database Engine» введите значение «localhost» или имя экземпляра SQL Server, после чего нажмите кнопку «Соединить».

  3. Вставьте следующий сценарий 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]

См. также

Основные понятия

Элемент Association (модель EDM)
Свойства навигации (модель EDM)
Реализация сущностей (модель EDM)

Другие ресурсы

Спецификация схем и сопоставлений (платформа Entity Framework)
Образцы приложений (платформа Entity Framework)