Implémentation d'associations (EDM)

Dans le modèle EDM (Modèle de données d'entité), les associations sont définies en langage CSDL (Conceptual Schema Definition Language). L'implémentation d'associations mappe les définitions en CSDL aux métadonnées de stockage et crée les associations à partir du schéma qui définit les entités qu'elles associent.

La manière dont les associations sont mappées à un stockage et initialisées dans du code et dont une application explore les données des associations est décrite dans cette rubrique et dans Code d'application utilisant des associations (EDM), grâce à l'examen d'un exemple simple, de bout en bout.

Association entre client, commande et ligne de commande

Dans les applications métier, les entités représentant les clients et les commandes présentent une relation logique. Les produits qu'achètent les clients sont représentés par des commandes. Les clients (customer) et les commandes (order) sont reliés à l'aide de l'élément Association nommé Order_Customer.

Chaque entité de l'association est appelée « terminaison » (End). Dans cette association, la terminaison (End) qui représente un client peut être reliée à de nombreuses commandes, mais la terminaison (End) qui représente les commandes ne peut être reliée qu'à un seul client. Cette relation est définie dans la syntaxe de schéma à l'aide de l'attribut Multiplicity. La terminaison (End) de cette association appelée Customers a un attribut Multiplicity="1", et l'autre End, appelée Orders un attribut Multiplicity="*". Il s'agit alors d'une association un-à-plusieurs. Pour plus d'informations sur les types d'associations, voir Association (EDM).

Après avoir défini l'association Order_Customer à l'aide d'éléments de schéma Association, l'association est incluse comme AssociationSet dans un EntityContainer avec les entités utilisées dans cet exemple. Pour plus d'informations sur les ensembles d'associations et les conteneurs d'entités, voir Élément EntityContainer (CSDL).

L'exemple de schéma ci-dessous définit des entités représentant les clients (Customers), les commandes (Orders) et les lignes de commande (OrderLines), ainsi que deux associations : Order_Customer et 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>

Dans l'association Order_Customer, un NavigationProperty a été implémenté sur l'entité Customers pour une exploration et une initialisation faciles de l'association. L'entité Orders spécifie aussi un NavigationProperty utilisé pour initialiser et explorer l'entité OrderLines qu'il contient. Pour plus d'informations sur la définition de NavigationProperty, voir Propriétés de navigation (EDM).

Mappage d'associations et métadonnées

Le mappage des entités Customers et Orders d'associations un-à-plusieurs, comme l'association Order_Customer, s'effectue dans cet exemple en mappant l'association à une relation de clé étrangère entre les tables Orders et Customers d'une base de données. Avec cette méthode, plusieurs instances de la clé étrangère de Customers sont contenues par la table Orders pour représenter les commandes associées à chaque client.

Schéma de stockage

Le schéma SSDL (Store Schema Definition Language) ci-dessous constitue les métadonnées représentant les tables Customers, Orders et OrderLines. Le schéma SSDL déclare ces tables à l'aide d'éléments EntityType correspondant aux tables de la base de données. Les types assignés aux propriétés des entités sont fonction des types de données du système de gestion de base de données. Par exemple, la propriété Name de l'entité Customers prend le type nvarchar au lieu du type String utilisé dans le schéma 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>

Ce schéma de stockage, écrit en SSDL, spécifie les types de données des entités Customers et Orders à mesure qu'elles sont implémentées dans la base de données. Les propriétés Key qui étaient de Type="Guid" dans le schéma CSDL prennent le Type="uniqueidentifier" dans la base de données et doivent être spécifiées en utilisant des types de base de données dans le schéma de stockage.

Les propriétés qui étaient de type String en CSDL sont mappées à des types nvarchar dans le stockage.

Les spécifications d'associations sont les mêmes dans le schéma de stockage et dans le schéma CSDL. Comme dans le schéma CSDL, les éléments End des associations sont déclarés avec les attributs Role, Type et Multiplicity. Les attributions pour End et Role sont les mêmes qu'en CSDL.

L'attribut ReferentialConstraint indique que les associations dépendent des structures de la base de données. ReferentialConstraint spécifie un Principle Role et des éléments Dependent Role et PropertyRef. Les attributs PropertyRef spécifient les propriétés des entités End représentant les colonnes de clé primaire et de clé étrangère des tables de base de données qui correspondent aux entités de l'Association. L'attribut PropertyRef du Principle Role spécifie la colonne qui contient la clé primaire. Par exemple, dans l'association Order_Customer, le PropertyRef de Principle Role est la propriété CustomerId. Cette propriété du schéma SSDL représente la colonne CustomerId de la table Customers qui sera affectée à la colonne de clé étrangère Customer de la table Orders. La clé étrangère est représentée par le PropertyRef du Dependent Role.

Spécification de mappage

Le mappage des associations ressemble à celui des entités.

Le segment MSL (Mapping Specification Language) ci-dessous montre le mappage AssociationSetMapping nommé Order_Customer. L'association OrderInfoModel.Order_Customer spécifiée dans le schéma CSDL est mappé, dans cet exemple, à la table Orders du schéma SSDL qui représente la base de données.

PropertyRef, du côté Customers de l'association, mappe explicitement la propriété CustomerId de l'entité Customers à la colonne Customer de la table Orders ; cette colonne contient la clé étrangère représentant la relation entre les tables Orders et Customers de la base de données.

L'exemple ci-dessous représente le schéma MSL complet dont a été extrait le segment précédent. Ce mappage comprend les éléments EntitySetMapping et AssociationSetMapping requis pour cet exemple.

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

Les segments de code présentés dans cette rubrique comprennent les schémas complets qui permettent de générer un modèle objet de programmation sur l'espace de noms OrderInfo et de le mapper au stockage. Pour obtenir des exemples de code qui utilisent ce modèle, voir Code d'application utilisant des associations (EDM).

Méthodes de classes partielles

Le modèle EDM permet d'implémenter des méthodes d'assistance dans des classes partielles afin d'améliorer la fonctionnalité d'applications. L'exemple ci-dessous est celui d'une méthode d'assistance de la classe Orders. Cette méthode d'assistance totalise les montants « prix global » (ExtendedPrice) des lignes de commande (OrderLines) d'une commande afin d'obtenir le montant total (TotalAmount) d'une commande (Order). Pour plus d'informations, voir Méthodes d'assistance (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;

        }
    }
}

Implémentation de la base de données

Le script ci-dessous peut être utilisé pour créer la base de données de cet exemple. Pour créer la base de données OrderInfo avec SQL Server Management Studio

  1. Dans le menu Fichier, pointez sur Nouveau, puis cliquez sur Requête de moteur de base de données.

  2. Dans la boîte de dialogue Se connecter au moteur de base de données, tapez localhost ou le nom de l'instance de SQL Server, puis cliquez sur Se connecter.

  3. Collez le script Transact-SQL fourni ci-dessous dans la fenêtre de requête, puis cliquez sur Exécuter.

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]

Voir aussi

Concepts

Association (EDM)
Propriétés de navigation (EDM)
Implémentation d'entités (EDM)

Autres ressources

Schémas et spécification de mappage (Entity Framework)
Exemples d'applications (Entity Framework)