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_Customer
association 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="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>
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="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>
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:
On the File menu, point to New, and then click Database Engine Query.
In the Connect to Database Engine dialog box, type either localhost or the name of the SQL Server instance, and then click Connect.
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)