Implementing Associations (EDM)

In the Entity Data Model (EDM), associations are defined in conceptual schema definition language (CSDL). Implementation of associations maps the definitions in CSDL to storage metadata and builds the associations from the same schema that defines the entities they associate.

How associations are mapped to storage and initialized in code and how an application navigates the association data is covered in this topic and the topic Application Code Using Associations (EDM) by examining a simple end-to-end example.

Customer, Order, Order Line Association

In line of business (LOB) applications, the entities representing customers and orders are logically related. The products customers purchase are represented by orders. Customers and orders are related using the Association element named Order_Customer.

Each entity in the association is called an End. In this association, the End of the association that represents a customer can be related to many orders, but the End representing orders can only relate to one customer. In schema syntax, this is defined by using the Multiplicity attribute. The End of this association called Customers has a Multiplicity="1", and the End called Orders has a Multiplicity="*". This is an example of a one-to-many association. For more information about association types, see Association (EDM).

After the Order_Customerassociation has been defined by using Association schema elements, the association is included as an AssociationSet in an EntityContainer along with the entities used in this example. For more information about association sets and entity containers, see EntityContainer Element (CSDL).

The following schema example defines entities representing Customers, Orders, OrderLines, and two associations: Order_Customer and OrderLine_Order.

<?xml version="1.0" encoding="utf-8"?>
      <Schema Namespace="OrderInfoModel" Alias="Self" 
              xmlns="http://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>

The Order_Customer association, a NavigationProperty has been implemented on the Customers entity to provide easy navigation and initialization of the association. The Orders entity also specifies a NavigationProperty used to initialize and navigate the OrderLines it contains. For more information about the NavigationProperty definition, see Navigation Properties (EDM).

Association Mapping and Metadata

Mapping the Customers and Orders of one-to-many associations, such as the Order_Customer association, is accomplished in this example by mapping the association to a foreign key relationship between the Orders and Customers tables in a database. By this method, multiple instances of the Customers foreign key are contained by the Orders table to represent the orders associated with each customer.

Storage Schema

The following store schema definition language (SSDL) schema is the storage metadata representing the Customers table, the Orders table, and the OrderLines table. The SSDL schema declares these tables using EntityType elements corresponding to the tables in the database. The properties of entities are typed according to the data types of the database management system. For example, the Name property of the Customers entity is typed as nvarchar instead of the String type that is used in the CSDL schema.

<?xml version="1.0" encoding="utf-8"?>
      <Schema Namespace="OrderInfoModel.Store" 
              Alias="Self" Provider="System.Data.SqlClient" 
              ProviderManifestToken="2005" 
xmlns:store="http://schemas.microsoft.com/ado/2007/12/edm/EntityStoreSchemaGenerator" 
              xmlns="http://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>

This storage schema, written in SSDL, specifies data types for the Customers and Orders entities as they are implemented in the database. Key properties that were of Type="Guid" in the CSDL schema have Type="uniqueidentifier" in the database and must be specified by using database types in the storage schema.

Properties that were String types in CSDL are mapped to nvarchar types in storage.

Association specifications are the same in the storage schema as in the CSDL schema. As in the CSDL schema, associations End elements are declared with Role, Type, and Multiplicity attributes. The End and Role assignments are the same as in CSDL.

The ReferentialConstraint attribute indicates that the associations depend on database structures. The ReferentialConstraint specifies a Principle Role and Dependent Role, and PropertyRef elements. The PropertyRef attributes specify the properties of the End entities that represent the primary key and foreign key columns of the database tables that correspond to the entities in the Association. The PropertyRef attribute of the Principle Role specifies the column that holds the primary key. For example, in the Order_Customer association, the PropertyRef of the Principle Role is the CustomerId property. This property in the SSDL schema represents the CustomerId column of the Customers table that will be assigned to the foreign key column Customer in the Orders table. The foreign key is represented by the PropertyRef of the Dependent Role.

Mapping Specification

Mapping associations resembles mapping entities.

The following mapping specification language (MSL) segment shows the AssociationSetMapping named Order_Customer. The OrderInfoModel.Order_Customer association specified in the CSDL schema is mapped, in this example, to the Orders table in the SSDL schema that represents the database.

The PropertyRef on the Customers side of the association explicitly maps the CustomerId property of the Customers entity to the Customer column of the Orders table; this column holds the foreign key representing the relationship between Orders and Customers tables in the database.

The following example shows the complete MSL schema from which the previous segment was extracted. This mapping includes both the EntitySetMapping and AssociationSetMapping elements required for this example.

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

The code segments shown in this topic include the complete schemas to build a programming object model on the OrderInfo namespace and map it to storage. For examples of code that uses this model, see Application Code Using Associations (EDM).

Partial Class Methods

In the EDM, helper methods can be implemented in partial classes to improve functionality of applications. The following example is a helper method of the Orders class. This helper method totals the ExtendedPrice amounts of OrderLines of an order to compute the TotalAmount of an Order. For more information, see Helper Methods (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;

        }
    }
}

Implementing the Database

The following script can be used to create the database for this example. To create the OrderInfo database with SQL Server Management Studio:

  1. On the File menu, point to New, and then click Database Engine Query.

  2. In the Connect to Database Engine dialog box, type either localhost or the name of the SQL Server instance, and then click Connect.

  3. Paste the following Transact-SQL script in the query window and then click Execute.

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]

See Also

Concepts

Association (EDM)
Navigation Properties (EDM)
Implementing Entities (EDM)

Other Resources

Schemas and Mapping Specification (Entity Framework)
Sample Applications (Entity Framework)