アソシエーションの実装 (EDM)
エンティティ データ モデル (EDM) では、アソシエーションが概念スキーマ定義言語 (CSDL) で定義されます。CSDL の定義は、アソシエーションの実装によってストレージ メタデータにマップされます。また、アソシエーションは、それによって関連付けられるエンティティを定義する同じスキーマから構築されます。
アソシエーションをストレージに対してどのようにマップし、コードで初期化するか、また、アプリケーションがアソシエーション データをどのようにナビゲートするかについては、このトピックおよび「アソシエーションを使ったアプリケーション コード (EDM)」で簡単なエンド ツー エンドの例を使って説明しています。
顧客、注文、注文明細のアソシエーション
基幹業務 (LOB) アプリケーションでは、顧客を表すエンティティと注文を表すエンティティ間に論理的な関係があります。顧客が購入する製品は、注文によって表されます。顧客と注文は、Order_Customer
という名前の Association 要素を使って関連付けられます。
アソシエーションの各エンティティは End と呼ばれます。このアソシエーションで顧客を表す End は、複数の注文に関連付けることができますが、注文を表す End は単一の顧客にしか関連付けることができません。スキーマ構文では、これを Multiplicity 属性を使って定義します。このアソシエーションの一方の End (Customers
) には Multiplicity="1"
が、もう一方の End (Orders
) には Multiplicity="*"
が割り当てられます。これは一対多のアソシエーションの例です。アソシエーションの種類の詳細については、「アソシエーション (EDM)」を参照してください。
Association スキーマ要素を使って Order_Customer
アソシエーションを定義した後、このアソシエーションが、この例で使用されている各種のエンティティと共に、EntityContainer 内に AssociationSet として追加されています。アソシエーション セットおよびエンティティ コンテナの詳細については、「EntityContainer 要素 (CSDL)」を参照してください。
次のスキーマ例では、Customers
(顧客)、Orders
(注文)、OrderLines
(注文明細) の各エンティティの他に、Order_Customer
と OrderLine_Order
の 2 つのアソシエーションが定義されています。
<?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
エンティティには、そこに含まれる OrderLines
の初期化とナビゲートに使用される NavigationProperty が指定されます。NavigationProperty 定義の詳細については、「ナビゲーション プロパティ (EDM)」を参照してください。
アソシエーションのマッピングとメタデータ
この例では、2 つのデータベース テーブル (Orders
と Customers
) 間の外部キー リレーションシップに対してアソシエーションをマップすることによって、Customers
と Orders
の一対多のアソシエーション (Order_Customer
アソシエーションなど) のマッピングを行います。この方法では、それぞれの顧客に関連付けられている注文を表すために、Orders
テーブルには、外部キー Customers
の複数のインスタンスが格納されます。
ストレージ スキーマ
次のストア スキーマ定義言語 (SSDL) スキーマは、Customers
テーブル、Orders
テーブル、および OrderLines
テーブルを表すストレージ メタデータです。これらのテーブルは、SSDL スキーマによって、データベース内のテーブルに対応する EntityType 要素を使用して宣言されています。エンティティのプロパティには、データベース管理システムのデータ型に応じた型が指定されています。たとえば、Customers
エンティティの Name プロパティには、CSDL スキーマで使用されている String 型ではなく、nvarchar 型が指定されています。
<?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
エンティティのデータ型が指定されています。CSDL スキーマでは Type="Guid"
型が指定されていた Key プロパティには、データベース内の Type="uniqueidentifier"
が割り当てられています。このように、ストレージ スキーマでは、データベースの型を使ってデータ型を指定する必要があります。
CSDL では String 型が指定されていたプロパティは、ストレージでは nvarchar 型にマップされています。
アソシエーションの仕様は、ストレージ スキーマも CSDL スキーマも同じです。CSDL スキーマと同様、アソシエーションの End 要素は、Role 属性、Type 属性、および Multiplicity 属性によって宣言されます。End と Role の割り当ては、CSDL と同じです。
ReferentialConstraint 属性は、アソシエーションがデータベースの構造に依存することを示します。ReferentialConstraint は、Principle Role と Dependent Role、および PropertyRef の各要素を指定します。PropertyRef 属性は、Association 内のエンティティに対応するデータベース テーブルの主キー列と外部キー列を表す End エンティティのプロパティを指定します。Principle Role の PropertyRef 属性は、主キーを格納する列を指定します。たとえば、Order_Customer
アソシエーションでは、Principle Role の PropertyRef は CustomerId
プロパティです。この SSDL スキーマでは、Orders
テーブル内の外部キー列 Customer
に割り当てられる、Customers
テーブルの CustomerId
列を表します。外部キーは、Dependent Role の PropertyRef で表されます。
マッピング スキーマ
アソシエーションのマッピングは、エンティティのマッピングと似ています。
以下に示す例は、マッピング スキーマ言語 (MSL) から、Order_Customer
という名前の AssociationSetMapping を定義した部分を抜粋したものです。この例では、CSDL スキーマに指定された OrderInfoModel.Order_Customer
アソシエーションが、データベースを表す SSDL スキーマの Orders
テーブルにマップされます。
アソシエーションの Customers
側の PropertyRef により、Customers
エンティティの CustomerId
プロパティが、Orders
テーブルの Customer
列へと明示的にマップされます。この列には、データベースの 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
クラスのヘルパ メソッドです。このヘルパ メソッドでは、注文明細 (OrderLines
) の小計 (ExtendedPrice
) を合計することによって、Order
の TotalAmount
を計算しています。詳細については、「ヘルパ メソッド (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;
}
}
}
データベースの実装
次のスクリプトを使用すると、この例のデータベースを作成できます。SQL Server Management Studio で OrderInfo データベースを作成するには、次の手順に従います。
[ファイル] メニューの [新規作成] をポイントし、[データベース エンジン クエリ] をクリックします。
[データベース エンジンへの接続] ダイアログ ボックスで 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]
参照
概念
アソシエーション (EDM)
ナビゲーション プロパティ (EDM)
エンティティの実装 (EDM)
その他のリソース
スキーマおよびマッピング スキーマ (Entity Framework)
サンプル アプリケーション (Entity Framework)