変更の保存と同時実行制御の管理 (Entity Framework)

既定では、Entity Framework は、オプティミスティック同時実行制御モデルを実装しています。 つまり、データがクエリされてからデータが更新されるまで、データ ソースのデータにロックが保持されません。 Entity Framework は、同時実行をチェックしないでオブジェクトの変更をデータベースに保存します。 同時実行が多いエンティティについては、次の例のように、ConcurrencyMode="fixed" の属性を使用してエンティティが概念レイヤーでプロパティを定義することをお勧めします。

<Property Name="Status" Type="Byte" Nullable="false" ConcurrencyMode="Fixed" />

この属性を使用すると、Entity Framework は、変更をデータベースに保存する前に、データベース内の変更をチェックします。 競合する変更がある場合は、OptimisticConcurrencyException が発生します。 詳細については、「オブジェクト コンテキストでデータの同時実行制御を管理する方法 (Entity Framework)」を参照してください。 ストアド プロシージャを使用してデータ ソースへの更新を行う Entity Data Model を定義しても、OptimisticConcurrencyException が発生する可能性があります。 この場合、更新を実行するときに使用するストアド プロシージャによって、行が更新されなかったことが報告されると例外が発生します。

高いレベルの同時実行制御シナリオで更新を行う場合は、Refresh を頻繁に呼び出すことをお勧めします。 Refresh を呼び出すと、変更が反映される方法が RefreshMode によって制御されます。 StoreWins オプションを使用すると、Entity Framework はオブジェクト キャッシュ内のすべてのデータをデータベースの対応する値で上書きします。 これに対し、ClientWins オプションを使用すると、キャッシュ内の元の値がデータ ソースの最新の値で置換されます。 そのため、キャッシュ内のデータに対して行われた変更とデータ ソース内の同じデータに対して行われた変更の間の競合が排除され、オブジェクト キャッシュ内のすべての変更済みデータがデータ ソースに安全に保存されます。

データソースへの更新によってオブジェクト コンテキスト内の他のオブジェクトに属するデータが変更される場合は、SaveChanges メソッドを呼び出してから Refresh メソッドを呼び出します。 たとえば AdventureWorks Sales モデルで新しい SalesOrderDetail が追加されると、トリガーによって SubTotal 列が更新されて新しい項目がある集計に反映されます。 この場合、Refresh メソッドを呼び出して、その注文の SalesOrderHeader オブジェクトを渡します。 これにより、トリガーで生成された値がオブジェクト コンテキストの SalesOrderHeader オブジェクトに送信されます。

Entity Framework は、キャッシュ内のオブジェクトに加えられた変更を追跡します。 SaveChanges メソッドを呼び出すと、Entity Framework はデータ ソースに変更をマージしようとします。 オブジェクトがキャッシュに追加された後またはキャッシュ内で更新された後、オブジェクト キャッシュのデータ変更がデータ ソース内の変更と競合すると、OptimisticConcurrencyException が発生して SaveChanges に失敗する可能性があります。 その結果、トランザクション全体がロールバックされます。 OptimisticConcurrencyException が発生した場合、次の例に示すように、Refresh を呼び出して、競合を解決するためにオブジェクト データ内のデータを維持するか (ClientWins)、オブジェクト キャッシュ内のデータをデータ ソースのデータで更新するか (StoreWins) を指定する必要があります。

Try
    ' Try to save changes, which may cause a conflict. 
    Dim num As Integer = context.SaveChanges()
    Console.WriteLine("No conflicts. " & num.ToString() & " updates saved.")
Catch generatedExceptionName As OptimisticConcurrencyException
    ' Resolve the concurrency conflict by refreshing the 
    ' object context before re-saving changes. 
    context.Refresh(RefreshMode.ClientWins, orders)

    ' Save changes. 
    context.SaveChanges()
    Console.WriteLine("OptimisticConcurrencyException handled and changes saved")
End Try
try
{
    // Try to save changes, which may cause a conflict.
    int num = context.SaveChanges();
    Console.WriteLine("No conflicts. " +
        num.ToString() + " updates saved.");
}
catch (OptimisticConcurrencyException)
{
    // Resolve the concurrency conflict by refreshing the 
    // object context before re-saving changes. 
    context.Refresh(RefreshMode.ClientWins, orders);

    // Save changes.
    context.SaveChanges();
    Console.WriteLine("OptimisticConcurrencyException "
    + "handled and changes saved");
}

ObjectContext に追加されたオブジェクトがデータ ソースで正常にデータを作成できない場合、SaveChanges によって UpdateException が生成されます。 これは、リレーションシップで指定される外部キーを持つ行が既に存在していると発生します。 これが発生すると、Refresh を使用してオブジェクト コンテキストで追加済みのオブジェクトを更新できなくなります。 代わりに、MergeOptionOverwriteChanges の値でオブジェクトを再度読み込みます。

オブジェクト コンテキストの管理の詳細については、「オブジェクト コンテキストでデータの同時実行制御を管理する方法 (Entity Framework)」を参照してください。

トランザクションをオプティミスティック同時実行の代替として使用することもできます。 詳細については、「接続とトランザクションの管理 (Entity Framework)」を参照してください。

このセクションの内容

オブジェクト コンテキストでデータの同時実行制御を管理する方法 (Entity Framework)

参照

概念

オブジェクトの使用 (Entity Framework)
オブジェクトの作成、追加、変更、および削除 (Entity Framework)