Реализация ассоциаций (модель 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, сделайте следующее.
В меню «Файл» укажите пункт «Создать» и выберите пункт «Запрос к ядру СУБД».
В диалоговом окне «Подключиться к компоненту Database Engine» введите значение «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]
См. также
Основные понятия
Элемент Association (модель EDM)
Свойства навигации (модель EDM)
Реализация сущностей (модель EDM)
Другие ресурсы
Спецификация схем и сопоставлений (платформа Entity Framework)
Образцы приложений (платформа Entity Framework)