オブジェクト間のリレーションシップを変更する方法 (Entity Framework)
このトピックでは、EntityReference オブジェクトを使用して、オブジェクト コンテキストで 2 つのオブジェクト間のリレーションシップを変更する方法について説明します。SaveChanges メソッドが呼び出されると、リレーションシップの変更は、関連付けられているテーブルの外部キーに対する変更としてデータベースに保存されます。このトピックでは、AssociationChanged イベントの処理方法についても説明します。
このトピックの例には、Adventure Works Sales Model が使用されています。この例のコードを実行するには、あらかじめプロジェクトに AdventureWorks Sales Model を追加し、エンティティ フレームワーク が使用されるようにプロジェクトを構成しておく必要があります。具体的な方法については、「Entity Framework プロジェクトを手動で構成する方法」および「Entity Data Model を手動で定義する方法 (Entity Framework)」の手順を参照してください。また、次の using ステートメント (Visual Basic の場合は Imports) をコードに追加する必要があります。
Imports System.ComponentModel
using System.ComponentModel;
例
この例では、EntityReference オブジェクトを使用して、SalesOrderHeader オブジェクトと、関連付けられている注文の出荷先住所を表す Address オブジェクトとのリレーションシップを変更する方法を示します。
'Define the order and new address IDs.
Dim orderId As Integer = 43669
Dim newAddressId As Integer = 26
Using context As New AdventureWorksEntities()
Try
' Get the billing address to change to.
Dim newAddress As Address = context.Address _
.Where("it.AddressID = @addressId", _
New ObjectParameter("addressId", newAddressId)) _
.First()
' Get the order being changed.
Dim order As SalesOrderHeader = context.SalesOrderHeader _
.Where("it.SalesOrderID = @orderId", _
New ObjectParameter("orderId", orderId)).First()
' Load the current billing address.
If Not order.Address1Reference.IsLoaded Then
order.Address1Reference.Load()
End If
' Write the current billing street address.
Console.WriteLine("Current street: " _
+ order.Address1.AddressLine1)
' Change the billing address.
If Not order.Address1.Equals(newAddress) Then
order.Address1 = newAddress
' Write the changed billing street address.
Console.WriteLine("Changed street: " _
+ order.Address1.AddressLine1)
End If
' If the address change succeeds, save the changes.
context.SaveChanges()
' Write the current billing street address.
Console.WriteLine("Current street: " _
+ order.Address1.AddressLine1)
Catch ex As ApplicationException
' Handle the exception raised in the ShippingAddress_Changed
' handler when the status of the order prevents the
' shipping address from being changed. Don't retry because
' the relationship is in an inconsistent state and calling
' SaveChanges() will result in an UpdateException.
Console.WriteLine(ex.ToString())
Catch ex As InvalidOperationException
Console.WriteLine(ex.ToString())
End Try
End Using
// Define the order and new address IDs.
int orderId = 43669;
int newAddressId = 26;
using (AdventureWorksEntities context
= new AdventureWorksEntities())
{
try
{
// Get the billing address to change to.
Address newAddress = context.Address
.Where("it.AddressID = @addressId",
new ObjectParameter("addressId", newAddressId))
.First();
// Get the order being changed.
SalesOrderHeader order = context.SalesOrderHeader
.Where("it.SalesOrderID = @orderId",
new ObjectParameter("orderId", orderId)).First();
// Load the current billing address.
if (!order.Address1Reference.IsLoaded)
{
order.Address1Reference.Load();
}
// Write the current billing street address.
Console.WriteLine("Current street: "
+ order.Address1.AddressLine1);
// Change the billing address.
if (!order.Address1.Equals(newAddress))
{
order.Address1 = newAddress;
// Write the changed billing street address.
Console.WriteLine("Changed street: "
+ order.Address1.AddressLine1);
}
// If the address change succeeds, save the changes.
context.SaveChanges();
// Write the current billing street address.
Console.WriteLine("Current street: "
+ order.Address1.AddressLine1);
}
catch (ApplicationException ex)
{
// Handle the exception raised in the ShippingAddress_Changed
// handler when the status of the order prevents the
// shipping address from being changed. Don't retry because
// the relationship is in an inconsistent state and calling
// SaveChanges() will result in an UpdateException.
Console.WriteLine(ex.ToString());
}
catch (InvalidOperationException ex)
{
Console.WriteLine(ex.ToString());
}
}
この例では前の例を発展させ、出荷先住所を表す Address オブジェクトの EntityReference で AssociationChanged イベントを処理することによって出荷先住所が変更された場合に注文状態を確認する方法を示します。注文状態が 3 より大きい場合、その注文は変更できずに例外が発生します。SalesOrderHeader 部分クラスのコンストラクタでデリゲートが定義され、このイベントのハンドラもこの部分クラスで実装されます。これにより、特定の注文で出荷先住所が変更されるたびに注文の状態が確認されるようになります。
SalesOrderHeader と Address のリレーションシップの他方の End で変更を検証するには、同様の手法を使用して、出荷先住所に関連付けられている SalesOrderHeader オブジェクトの EntityCollection で AssociationChanged イベントを登録します。
Partial Public Class SalesOrderHeader
' SalesOrderHeader default constructor.
Public Sub New()
' Register the handler for changes to the
' shipping address (Address1) reference.
AddHandler Me.Address1Reference.AssociationChanged, _
AddressOf ShippingAddress_Changed
End Sub
' AssociationChanged handler for the relationship
' between the order and the shipping address.
Private Sub ShippingAddress_Changed(ByVal sender As Object, _
ByVal e As CollectionChangeEventArgs)
' Check for a related reference being removed.
If e.Action = CollectionChangeAction.Remove Then
' Check the order status and raise an exception if
' the order can no longer be changed.
If Me.Status > 3 Then
Throw New ApplicationException( _
"The shipping address cannot " _
+ "be changed because the order has either " _
+ "already been shipped or has been cancelled.")
End If
End If
End Sub
End Class
public partial class SalesOrderHeader
{
// SalesOrderHeader default constructor.
public SalesOrderHeader()
{
// Register the handler for changes to the
// shipping address (Address1) reference.
this.Address1Reference.AssociationChanged
+= new CollectionChangeEventHandler(ShippingAddress_Changed);
}
// AssociationChanged handler for the relationship
// between the order and the shipping address.
private void ShippingAddress_Changed(object sender,
CollectionChangeEventArgs e)
{
// Check for a related reference being removed.
if (e.Action == CollectionChangeAction.Remove)
{
// Check the order status and raise an exception if
// the order can no longer be changed.
if (this.Status > 3)
{
throw new ApplicationException(
"The shipping address cannot "
+ "be changed because the order has either "
+ "already been shipped or has been cancelled.");
}
}
}
}