Condividi tramite


Implementazione delle associazioni (EDM)

In Entity Data Model (EDM) le associazioni sono definite nel linguaggio CSDL (Conceptual Schema Definition Language). L'implementazione delle associazioni comporta il mapping delle definizioni in CSDL ai metadati di archiviazione e la compilazione di associazioni dallo stesso schema che definisce le entità associate.

Esaminando un semplice esempio end-to-end, in questo argomento e nell'argomento Utilizzo di associazioni nel codice di un'applicazione (EDM) viene descritto il modo in cui le associazioni vengono mappate all'archivio e inizializzate nel codice, nonché il modo in cui in un'applicazione vengono esplorati i dati delle associazioni.

Associazione tra cliente, ordine e riga dell'ordine

Nelle applicazioni line-of-business (LOB) le entità che rappresentano clienti e ordini sono correlate logicamente. I prodotti acquistati dai clienti sono rappresentati dagli ordini. I clienti e gli ordini sono correlati tramite l'elemento Association denominato Order_Customer.

Nell'associazione ogni entità è definita End. In questa associazione l'oggetto End che rappresenta un cliente può essere correlato a molti ordini, mentre l'oggetto End che rappresenta gli ordini può riferirsi a un solo cliente. Nella sintassi dello schema questa relazione viene definita utilizzando l'attributo Multiplicity. L'oggetto End di questa associazione denominato Customers ha l'attributo Multiplicity="1", mentre l'oggetto End denominato Orders ha l'attributo Multiplicity="*". Questo è un esempio di associazione uno-a-molti. Per ulteriori informazioni sui tipi di associazione, vedere Associazione (EDM).

Dopo aver definito l'associazione Order_Customer utilizzando gli elementi dello schema Association, l'associazione viene inclusa come AssociationSet in un EntityContainer insieme alle entità utilizzate in questo esempio. Per ulteriori informazioni sui set di associazioni e i contenitori di entità, vedere Elemento EntityContainer (CSDL).

Nell'esempio di schema seguente vengono definite entità che rappresentano Customers, Orders, OrderLines e due associazioni, ovvero Order_Customer e 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>

All'interno dell'associazione Order_Customer, è stato implementato un oggetto NavigationProperty per l'entità Customers allo scopo di semplificare le operazioni di navigazione e inizializzazione dell'associazione. Anche l'entità Orders specifica un oggetto NavigationProperty utilizzato per le operazioni di inizializzazione e navigazione degli oggetti OrderLines in esso contenuti. Per ulteriori informazioni sulla definizione dell'oggetto NavigationProperty, vedere Proprietà di navigazione (EDM).

Metadati e mapping di associazioni

Il mapping degli oggetti Customers e Orders delle associazioni uno-a-molti, ad esempio l'associazione Order_Customer, viene eseguito in questo esempio tramite il mapping dell'associazione a una relazione di chiave esterna tra le tabelle Orders e Customers in un database. Tramite questo metodo, più istanze della chiave esterna Customers sono contenute nella tabella Orders per rappresentare gli ordini associati a ogni cliente.

Schema di archiviazione

Lo schema SSDL (Store Schema Definition Language) seguente costituisce i metadati di archiviazione che rappresentano la tabella Customers, la tabella Orders e la tabella OrderLines. Lo schema SSDL dichiara tali tabelle utilizzando gli elementi EntityType corrispondenti alle tabelle nel database. Le proprietà delle entità sono tipizzate in base ai tipi di dati del sistema di gestione di database. La proprietà Name dell'entità Customers viene ad esempio tipizzata come nvarchar al posto del tipo String utilizzato nello schema 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>

Questo schema di archiviazione, scritto in SSDL specifica i tipi di dati per le entità Customers e Orders così come sono implementati nel database. Le proprietà Key con Type="Guid" nello schema CSDL presentano Type="uniqueidentifier" nel database e devono essere specificate utilizzando i tipi di database nello schema di archiviazione.

Le proprietà di tipo String in CSDL sono mappate ai tipi nvarchar nell'archivio.

Le specifiche di associazione sono le stesse nello schema di archiviazione e nello schema CSDL. Come nello schema CSDL, gli elementi End delle associazioni vengono dichiarati con gli attributi Role, Type e Multiplicity. Le assegnazioni End e Role corrispondono a quelle in CSDL.

L'attributo ReferentialConstraint indica che le associazioni dipendono dalle strutture di database. ReferentialConstraint specifica un Principle Role e un Dependent Role, nonché gli elementi PropertyRef. Gli attributi PropertyRef specificano le proprietà delle entità End che rappresentano le colonne di chiave primaria e di chiave esterna delle tabelle di database corrispondenti alle entità dell'oggetto Association. L'attributo PropertyRef di Principle Role specifica la colonna che contiene la chiave primaria. Nell'associazione Order_Customer, ad esempio, l'attributo PropertyRef dell'oggetto Principle Role è la proprietà CustomerId. Questa proprietà nello schema SSDL rappresenta la colonna CustomerId della tabella Customers che verrà assegnata alla colonna di chiave esterna Customer nella tabella Orders. La chiave esterna è rappresentata dall'attributo PropertyRef di Dependent Role.

Specifica di mapping

Il mapping di associazioni è simile al mapping di entità.

Nel segmento MSL (Mapping Specification Language) seguente viene illustrato l'oggetto AssociationSetMapping denominato Order_Customer. L'associazione OrderInfoModel.Order_Customer specificata nello schema CSDL viene mappata, in questo esempio, alla tabella Orders nello schema SSDL che rappresenta il database.

L'oggetto PropertyRef sul lato Customers dell'associazione esegue esplicitamente il mapping della proprietà CustomerId dell'entità Customers alla colonna Customer della tabella Orders. Tale colonna contiene la chiave esterna che rappresenta la relazione tra le tabelle Orders e Customers nel database.

Nell'esempio seguente viene illustrato lo schema MSL completo da cui è stato estratto il segmento precedente. Questo mapping include entrambi gli elementi EntitySetMapping e AssociationSetMapping necessari per l'esempio.

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

I segmenti di codice illustrati in questo argomento includono gli schemi completi per compilare un modello a oggetti di programmazione nello spazio dei nomi OrderInfo ed eseguirne il mapping all'archivio. Per esempi di codice che utilizzano questo modello, vedere Utilizzo di associazioni nel codice di un'applicazione (EDM).

Metodi di classi parziali

In EDM i metodi di supporto possono essere implementati nelle classi parziali per migliorare la funzionalità delle applicazioni. Nell'esempio seguente viene illustrato un metodo di supporto della classe Orders che consente di sommare gli importi ExtendedPrice di OrderLines di un ordine per calcolare il valore TotalAmount di un oggetto Order. Per ulteriori informazioni, vedere Metodi di supporto (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;

        }
    }
}

Implementazione del database

Lo script seguente può essere utilizzato per creare il database per questo esempio. Per creare il database OrderInfo con SQL Server Management Studio:

  1. Scegliere Nuovo dal menu File, quindi fare clic su Query del Motore di database.

  2. Nella finestra di dialogo Connetti al Motore di database digitare localhost oppure il nome dell'istanza di SQL Server, quindi fare clic su Connetti.

  3. Incollare lo script Transact-SQL seguente nella finestra di query, quindi fare clic su Esegui.

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]

Vedere anche

Concetti

Associazione (EDM)
Proprietà di navigazione (EDM)
Implementazione di entità (EDM)

Altre risorse

Specifica di schemi e mapping (Entity Framework)
Applicazioni di esempio (Entity Framework)