変更を保存するときにビジネス ロジックを実行する方法 (Entity Framework)
Entity Framework を使用すると、変更内容をデータベースに保存する前にカスタム ビジネス ロジックを実行できます。 SavingChanges イベントは、SaveChanges 操作が処理される前に発生します。 このイベントを処理して、変更内容をデータベースに保存する前にカスタム ビジネス ロジックを実装します。 .NET Framework バージョン 4 以降、SaveChanges メソッドは virtual になります。 つまり、SavingChanges イベントをサブスクライブしなくても、直接このメソッドをオーバーライドできるようになります。
このトピックの例は、SaveChanges メソッドをオーバーライドして SavingChanges イベントを処理し、オブジェクト コンテキスト内の変更されたオブジェクトがデータベースに保存される前に、これらの変更を検証する方法を示します。
このトピックの例には、Adventure Works Sales Model が使用されています。 この例のコードを実行するには、あらかじめプロジェクトに AdventureWorks Sales Model を追加し、Entity Framework が使用されるようにプロジェクトを構成しておく必要があります。 具体的な方法については、「Entity Framework プロジェクトを手動で構成する方法」および「方法: モデル ファイルとマッピング ファイルを手動で定義する (Entity Framework)」の手順を参照してください。
最初の例は、カスタム オブジェクトのコンテキスト クラスで SaveChanges メソッドをオーバーライドする方法を示しています。 2 番目の例は、OnContextCreated を使用して ObjectContext のインスタンスで SavingChanges イベントのハンドラーを登録する方法を示しています。 3 番目の例は、ObjectContext から派生するプロキシ クラスでこのイベントを処理する方法を示しています。 4 番目の例は、2 番目の例のプロキシ クラスを使用してオブジェクトに変更を加え、SaveChanges を呼び出すコードを示しています。
例
この例では、SaveChanges メソッドがオーバーライドされることにより、Added または Modified EntityState を持つオブジェクトが検証できるようになります。 この例は 方法: カスタム オブジェクト コンテキストを定義する (Entity Framework) で定義されるカスタム オブジェクト コンテキストを基にしています。
Public Overloads Overrides Function SaveChanges(ByVal options As SaveOptions) As Integer
For Each entry As ObjectStateEntry In ObjectStateManager.GetObjectStateEntries(EntityState.Added Or EntityState.Modified)
' Validate the objects in the Added and Modified state
' if the validation fails throw an exeption.
Next
Return MyBase.SaveChanges(options)
End Function
public override int SaveChanges(SaveOptions options)
{
foreach (ObjectStateEntry entry in
ObjectStateManager.GetObjectStateEntries(
EntityState.Added | EntityState.Modified))
{
// Validate the objects in the Added and Modified state
// if the validation fails throw an exeption.
}
return base.SaveChanges(options);
}
この例では、OnContextCreated メソッドは AdventureWorksEntities の部分メソッドとして定義されます。 SavingChanges イベントのハンドラーは、この部分メソッドで定義されます。 イベント ハンドラーは、変更が保存される前に、呼び出し元コードによって不適切なテキストが SalesOrderHeader.Comment プロパティに追加されていないことを確認します。 文字列をチェックするアルゴリズム (ここでは示されていません) によって問題が検出された場合、例外が発生します。
Partial Public Class AdventureWorksEntities
Private Sub OnContextCreated()
' Register the handler for the SavingChanges event.
AddHandler Me.SavingChanges, AddressOf context_SavingChanges
End Sub
' SavingChanges event handler.
Private Shared Sub context_SavingChanges(ByVal sender As Object, ByVal e As EventArgs)
' Validate the state of each entity in the context
' before SaveChanges can succeed.
For Each entry As ObjectStateEntry In DirectCast(sender, ObjectContext).ObjectStateManager.GetObjectStateEntries(EntityState.Added Or EntityState.Modified)
' Find an object state entry for a SalesOrderHeader object.
If Not entry.IsRelationship AndAlso (entry.Entity.GetType() Is GetType(SalesOrderHeader)) Then
Dim orderToCheck As SalesOrderHeader = TryCast(entry.Entity, SalesOrderHeader)
' Call a helper method that performs string checking
' on the Comment property.
Dim textNotAllowed As String = Validator.CheckStringForLanguage(orderToCheck.Comment)
' If the validation method returns a problem string, raise an error.
If textNotAllowed <> String.Empty Then
Throw New ArgumentException(String.Format("Changes cannot be " & _
"saved because the {0} '{1}' object contains a " & _
"string that is not allowed in the property '{2}'.", _
entry.State, "SalesOrderHeader", "Comment"))
End If
End If
Next
End Sub
End Class
public partial class AdventureWorksEntities
{
partial void OnContextCreated()
{
// Register the handler for the SavingChanges event.
this.SavingChanges
+= new EventHandler(context_SavingChanges);
}
// SavingChanges event handler.
private static void context_SavingChanges(object sender, EventArgs e)
{
// Validate the state of each entity in the context
// before SaveChanges can succeed.
foreach (ObjectStateEntry entry in
((ObjectContext)sender).ObjectStateManager.GetObjectStateEntries(
EntityState.Added | EntityState.Modified))
{
// Find an object state entry for a SalesOrderHeader object.
if (!entry.IsRelationship && (entry.Entity.GetType() == typeof(SalesOrderHeader)))
{
SalesOrderHeader orderToCheck = entry.Entity as SalesOrderHeader;
// Call a helper method that performs string checking
// on the Comment property.
string textNotAllowed = Validator.CheckStringForLanguage(
orderToCheck.Comment);
// If the validation method returns a problem string, raise an error.
if (textNotAllowed != string.Empty)
{
throw new ArgumentException(String.Format("Changes cannot be "
+ "saved because the {0} '{1}' object contains a "
+ "string that is not allowed in the property '{2}'.",
entry.State, "SalesOrderHeader", "Comment"));
}
}
}
}
}
この例では、AdventureWorksProxy クラスのインスタンスは、SalesOrderHeader オブジェクトの Comment プロパティを変更するために使用されます。 AdventureWorksProxy クラスによって提供されている ObjectContext のインスタンスに対して SaveChanges が呼び出されるとき、前の例の検証コードが実行されます。
' Create an instance of the proxy class that returns an object context.
Dim context As New AdventureWorksProxy()
' Get the first order from the context.
Dim order As SalesOrderHeader = context.Context.SalesOrderHeaders.First()
' Add some text that we want to catch before saving changes.
order.Comment = "some text"
Try
' Save changes using the proxy class.
Dim changes As Integer = context.Context.SaveChanges()
Catch ex As InvalidOperationException
' Handle the exception returned by the proxy class
' validation if a problem string is found.
Console.WriteLine(ex.ToString())
// Create an instance of the proxy class that returns an object context.
AdventureWorksProxy context = new AdventureWorksProxy();
// Get the first order from the context.
SalesOrderHeader order =
context.Context.SalesOrderHeaders.First();
// Add some text that we want to catch before saving changes.
order.Comment = "some text";
try
{
// Save changes using the proxy class.
int changes = context.Context.SaveChanges();
}
catch (InvalidOperationException ex)
{
// Handle the exception returned by the proxy class
// validation if a problem string is found.
Console.WriteLine(ex.ToString());
}
この例では、ObjectContext から派生するプロキシ クラスのオブジェクトに変更を加えてから、SaveChanges を呼び出します。 この例は、前の例で示したイベント処理を呼び出すために使用します。
Public Class AdventureWorksProxy
' Define the object context to be provided.
Private contextProxy As New AdventureWorksEntities()
Public Sub New()
' When the object is initialized, register the
' handler for the SavingChanges event.
AddHandler contextProxy.SavingChanges, AddressOf context_SavingChanges
End Sub
' Method that provides an object context.
Public ReadOnly Property Context() As AdventureWorksEntities
Get
Return contextProxy
End Get
End Property
' SavingChanges event handler.
Private Sub context_SavingChanges(ByVal sender As Object, ByVal e As EventArgs)
' Ensure that we are passed an ObjectContext
Dim context As ObjectContext = TryCast(sender, ObjectContext)
If context IsNot Nothing Then
' Validate the state of each entity in the context
' before SaveChanges can succeed.
For Each entry As ObjectStateEntry In context.ObjectStateManager.GetObjectStateEntries(EntityState.Added Or EntityState.Modified)
' Find an object state entry for a SalesOrderHeader object.
If Not entry.IsRelationship AndAlso (entry.Entity.GetType() Is GetType(SalesOrderHeader)) Then
Dim orderToCheck As SalesOrderHeader = TryCast(entry.Entity, SalesOrderHeader)
' Call a helper method that performs string checking
' on the Comment property.
Dim textNotAllowed As String = Validator.CheckStringForLanguage(orderToCheck.Comment)
' If the validation method returns a problem string, raise an error.
If textNotAllowed <> String.Empty Then
Throw New ArgumentException(String.Format("Changes cannot be " & _
"saved because the {0} '{1}' object contains a " & _
"string that is not allowed in the property '{2}'.", _
entry.State, "SalesOrderHeader", "Comment"))
End If
End If
Next
End If
End Sub
End Class
public class AdventureWorksProxy
{
// Define the object context to be provided.
private AdventureWorksEntities contextProxy =
new AdventureWorksEntities();
public AdventureWorksProxy()
{
// When the object is initialized, register the
// handler for the SavingChanges event.
contextProxy.SavingChanges
+= new EventHandler(context_SavingChanges);
}
// Method that provides an object context.
public AdventureWorksEntities Context
{
get
{
return contextProxy;
}
}
// SavingChanges event handler.
private void context_SavingChanges(object sender, EventArgs e)
{
// Ensure that we are passed an ObjectContext
ObjectContext context = sender as ObjectContext;
if (context != null)
{
// Validate the state of each entity in the context
// before SaveChanges can succeed.
foreach (ObjectStateEntry entry in
context.ObjectStateManager.GetObjectStateEntries(
EntityState.Added | EntityState.Modified))
{
// Find an object state entry for a SalesOrderHeader object.
if (!entry.IsRelationship && (entry.Entity.GetType() == typeof(SalesOrderHeader)))
{
SalesOrderHeader orderToCheck = entry.Entity as SalesOrderHeader;
// Call a helper method that performs string checking
// on the Comment property.
string textNotAllowed = Validator.CheckStringForLanguage(
orderToCheck.Comment);
// If the validation method returns a problem string, raise an error.
if (textNotAllowed != string.Empty)
{
throw new ArgumentException(String.Format("Changes cannot be "
+ "saved because the {0} '{1}' object contains a "
+ "string that is not allowed in the property '{2}'.",
entry.State, "SalesOrderHeader", "Comment"));
}
}
}
}
}
}
参照
処理手順
方法: オブジェクト状態が変化したときにビジネス ロジックを実行する
スカラー プロパティの変更時にビジネス ロジックを実行する方法 (Entity Framework)
方法: アソシエーションの変更時にビジネス ロジックを実行する