EF5 Code First を使用して一貫性のある複数リーダー/ライター カウンターを実装しようとしていますが、同時実行例外が発生しています (これは予想していました) が、予期していなかった主キー制約違反も発生しています。これは、カウンターがコードによって作成された場合にのみ発生します。すでに存在する場合、カウントは期待どおりに行われます。
これが私が使用しているコードです(デバッグコードも含む):
public class EFCounter
{
private static int UpdateExceptionCount = 0;
private const int StartValue = 1000001;
public int CreateOrIncrement(Guid counterId)
{
using (var context = new EFCounterContext("MsSqlViewModel"))
{
if (context.Counters.Any(cntr => cntr.CounterId == counterId) == false)
{
try
{
context.Counters.Add(
new Counter
{
CounterId = counterId,
Value = StartValue
}
);
context.SaveChanges();
return StartValue;
}
catch (Exception e)
{
//fall through
}
}
var objectContext = ((IObjectContextAdapter) context).ObjectContext;
var counter = context.Counters.First(cntr => cntr.CounterId == counterId);
do
{
try
{
lock (this)
{
counter.Value += 1;
objectContext.SaveChanges(SaveOptions.DetectChangesBeforeSave);
return counter.Value;
}
}
catch (OptimisticConcurrencyException ex)
{
objectContext.Refresh(RefreshMode.StoreWins, counter);
}
catch (UpdateException ex)
{
var ueCount = Interlocked.Increment(ref UpdateExceptionCount);
objectContext.Detach(counter);
counter = context.Counters.First(cntr => cntr.CounterId == counterId);
Console.WriteLine("UpdateExceptions: {0}", ueCount);
}
} while (true);
}
}
}
public class Counter
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public Guid CounterId { get; set; }
[ConcurrencyCheck]
public int Value { get; set; }
}
私は単にParallel.For
それを呼び出すために使用しています:
EFCounter counter1 = new EFCounter();
EFCounter counter2 = new EFCounter();
Guid counterId = Guid.NewGuid();
Parallel.For(
1,
10,
i =>
{
Console.WriteLine("1: {0}", counter1.CreateOrIncrement(counterId));
Console.WriteLine("2: {0}", counter2.CreateOrIncrement(counterId));
}
);
参考までに、接続文字列は次のとおりです。
<add name="MsSqlViewModel" providerName="System.Data.SqlClient" connectionString="Data Source=localhost;Initial Catalog=ESRaffleViewModels;Integrated Security=SSPI" />
これは、私のマシンでの代表的な実行の出力です。UpdateExceptions が表示された場所で実際に実行を完了したことはありません。
1: 1000001 2: 1000002 UpdateExceptions: 1 1: 1000003 UpdateExceptions: 2 2: 1000004 UpdateExceptions: 3 1: 1000005 UpdateExceptions: 4 2: 1000006 UpdateExceptions: 5 UpdateExceptions: 6 UpdateExceptions: 7 UpdateExceptions: 8 UpdateExceptions: 9 UpdateExceptions: 10 更新例外: 11 更新例外: 12 更新例外: 13 更新例外: 14 UpdateExceptions: 15 更新例外: 16 更新例外: 17 更新例外: 18 更新例外: 19 UpdateExceptions: 20 更新例外: 21 更新例外: 22 更新例外: 23 UpdateExceptions: 24 UpdateExceptions: 25 更新例外: 26 更新例外: 27 更新例外: 28 更新例外: 29 更新例外: 30 更新例外: 31 更新例外: 32 更新例外: 33 UpdateExceptions: 34 更新例外: 35 更新例外: 36 更新例外: 37 更新例外: 38 更新例外: 39 更新例外: 40 更新例外: 41 UpdateExceptions: 42 更新例外: 43 UpdateExceptions: 44 更新例外: 45 更新例外: 46 更新例外: 47 更新例外: 48 更新例外: 49 UpdateExceptions: 50 更新例外: 51 更新例外: 52 更新例外: 53 UpdateExceptions: 54 更新例外: 55 更新例外: 56 更新例外: 57 更新例外: 58 更新例外: 59 更新例外: 60 UpdateExceptions: 61 UpdateExceptions: 62 UpdateExceptions: 63 UpdateExceptions: 64 UpdateExceptions: 65 UpdateExceptions: 66 更新例外: 67 UpdateExceptions: 68 更新例外: 69 更新例外: 70 更新例外: 71 更新例外: 72 更新例外: 73 更新例外: 74 更新例外: 75 更新例外: 76 更新例外: 77 更新例外: 78 更新例外: 79 更新例外: 80 更新例外: 81 UpdateExceptions: 82 UpdateExceptions: 83 UpdateExceptions: 84 更新例外: 85 更新例外: 86 更新例外: 87 UpdateExceptions: 88 UpdateExceptions: 89 更新例外: 90 更新例外: 91 更新例外: 92 更新例外: 93 更新例外: 94 更新例外: 95 更新例外: 96 更新例外: 97 更新例外: 98 更新例外: 99 更新例外: 100
ここで何か基本的なことが欠けていますか?