整数フィールドをインクリメントまたはデクリメントするクリーンで安全な方法は何ですか?
私はエンティティフレームワーク5.xと組み合わせてSQLサーバー2012を使用しています
連動インクリメント/デクリメントに相当するものを探します。
整数フィールドをインクリメントまたはデクリメントするクリーンで安全な方法は何ですか?
私はエンティティフレームワーク5.xと組み合わせてSQLサーバー2012を使用しています
連動インクリメント/デクリメントに相当するものを探します。
標準的な方法は、楽観的並行性を使用することです。
インクリメントまたはデクリメントする整数フィールドを持つ単純なエンティティがあるとしCounter
ます。
public class SomeEntity
{
public int SomeEntityId { get; set; }
public int Counter { get; set; }
}
Counter
then を同時実行トークンとしてマークできます。Fluent API の場合:
modelBuilder.Entity<SomeEntity>()
.Property(s => s.Counter)
.IsConcurrencyToken();
Counter
次に、たとえば次のようにインクリメントまたはデクリメントできます。
public void IncDecCounter(int someEntityId, bool dec)
{
using (var context = new MyContext())
{
var someEntity = context.SomeEntities.Find(someEntityId);
if (someEntity != null)
{
bool saveFailed;
do
{
saveFailed = false;
if (dec)
--someEntity.Counter;
else
++someEntity.Counter;
try
{
context.SaveChanges();
}
catch (DbUpdateConcurrencyException e)
{
saveFailed = true;
e.Entries.Single().Reload();
}
} while (saveFailed);
}
}
}
SaveChanges
エンティティがロードされたときの の値 (DbUpdateConcurrencyException
この例では、他のクエリである可能性があります) が、データベースで UPDATE ステートメントが実行されたときのデータベースの の値と異なる場合、 で失敗します。その間に別のユーザーによって変更されました。技術的には、これは生成された UPDATE ステートメントの拡張句によって実現されます。これは、Id だけでなく の古い値によってもフィルタリングを試みます。基本的には次のようなものです。Counter
Find
Counter
Counter
WHERE
Counter
WHERE SomeEntityId = someEntityId AND Counter = oldCounterWhenTheEntityHasBeenLoaded
ブロックは、データベースからcatch
の現在の値でエンティティを再読み込みしCounter
、次のループは、同時実行性違反なしで成功するまで、再読み込みされた値を再度インクリメントまたはデクリメントしようとします。