EF5中实现的多线程计数器的主键冲突

时间:2012-11-20 18:14:19

标签: c# ef-code-first

我正在尝试使用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" />

以下是我机器上代表性运行的输出;我从未真正完成过RunExceptions出现的运行:

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
UpdateExceptions: 11
UpdateExceptions: 12
UpdateExceptions: 13
UpdateExceptions: 14
UpdateExceptions: 15
UpdateExceptions: 16
UpdateExceptions: 17
UpdateExceptions: 18
UpdateExceptions: 19
UpdateExceptions: 20
UpdateExceptions: 21
UpdateExceptions: 22
UpdateExceptions: 23
UpdateExceptions: 24
UpdateExceptions: 25
UpdateExceptions: 26
UpdateExceptions: 27
UpdateExceptions: 28
UpdateExceptions: 29
UpdateExceptions: 30
UpdateExceptions: 31
UpdateExceptions: 32
UpdateExceptions: 33
UpdateExceptions: 34
UpdateExceptions: 35
UpdateExceptions: 36
UpdateExceptions: 37
UpdateExceptions: 38
UpdateExceptions: 39
UpdateExceptions: 40
UpdateExceptions: 41
UpdateExceptions: 42
UpdateExceptions: 43
UpdateExceptions: 44
UpdateExceptions: 45
UpdateExceptions: 46
UpdateExceptions: 47
UpdateExceptions: 48
UpdateExceptions: 49
UpdateExceptions: 50
UpdateExceptions: 51
UpdateExceptions: 52
UpdateExceptions: 53
UpdateExceptions: 54
UpdateExceptions: 55
UpdateExceptions: 56
UpdateExceptions: 57
UpdateExceptions: 58
UpdateExceptions: 59
UpdateExceptions: 60
UpdateExceptions: 61
UpdateExceptions: 62
UpdateExceptions: 63
UpdateExceptions: 64
UpdateExceptions: 65
UpdateExceptions: 66
UpdateExceptions: 67
UpdateExceptions: 68
UpdateExceptions: 69
UpdateExceptions: 70
UpdateExceptions: 71
UpdateExceptions: 72
UpdateExceptions: 73
UpdateExceptions: 74
UpdateExceptions: 75
UpdateExceptions: 76
UpdateExceptions: 77
UpdateExceptions: 78
UpdateExceptions: 79
UpdateExceptions: 80
UpdateExceptions: 81
UpdateExceptions: 82
UpdateExceptions: 83
UpdateExceptions: 84
UpdateExceptions: 85
UpdateExceptions: 86
UpdateExceptions: 87
UpdateExceptions: 88
UpdateExceptions: 89
UpdateExceptions: 90
UpdateExceptions: 91
UpdateExceptions: 92
UpdateExceptions: 93
UpdateExceptions: 94
UpdateExceptions: 95
UpdateExceptions: 96
UpdateExceptions: 97
UpdateExceptions: 98
UpdateExceptions: 99
UpdateExceptions: 100

我错过了一些基本的东西吗?

1 个答案:

答案 0 :(得分:-1)

锁定(this)并不是EFCounter的两个实例所独有的,因此基本上没用。你需要一个静态对象。

您需要使用数据库来处理数据库并发。