11

整数フィールドをインクリメントまたはデクリメントするクリーンで安全な方法は何ですか?

私はエンティティフレームワーク5.xと組み合わせてSQLサーバー2012を使用しています

連動インクリメント/デクリメントに相当するものを探します。

4

1 に答える 1

20

標準的な方法は、楽観的並行性を使用することです。

インクリメントまたはデクリメントする整数フィールドを持つ単純なエンティティがあるとしCounterます。

public class SomeEntity
{
    public int SomeEntityId { get; set; }
    public int Counter { get; set; }
}

Counterthen を同時実行トークンとしてマークできます。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 だけでなく の古い値によってもフィルタリングを試みます。基本的には次のようなものです。CounterFindCounterCounterWHERECounterWHERE SomeEntityId = someEntityId AND Counter = oldCounterWhenTheEntityHasBeenLoaded

ブロックは、データベースからcatchの現在の値でエンティティを再読み込みしCounter、次のループは、同時実行性違反なしで成功するまで、再読み込みされた値を再度インクリメントまたはデクリメントしようとします。

于 2013-03-27T21:26:27.967 に答える