0

我正在尝试使用 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
更新异常:1
1:1000003
更新异常:2
2:1000004
更新异常:3
1:1000005
更新异常:4
2:1000006
更新异常:5
更新异常:6
更新异常:7
更新异常:8
更新异常:9
更新异常:10
更新异常:11
更新异常:12
更新异常:13
更新异常:14
更新异常:15
更新异常:16
更新异常:17
更新异常:18
更新异常:19
更新异常:20
更新异常:21
更新异常:22
更新异常:23
更新异常:24
更新异常:25
更新异常:26
更新异常:27
更新异常:28
更新异常:29
更新异常:30
更新异常:31
更新异常:32
更新异常:33
更新异常:34
更新异常:35
更新异常:36
更新异常:37
更新异常:38
更新异常:39
更新异常:40
更新异常:41
更新异常:42
更新异常:43
更新异常:44
更新异常:45
更新异常:46
更新异常:47
更新异常:48
更新异常:49
更新异常:50
更新异常:51
更新异常:52
更新异常:53
更新异常:54
更新异常:55
更新异常:56
更新异常:57
更新异常:58
更新异常:59
更新异常:60
更新异常:61
更新异常:62
更新异常:63
更新异常:64
更新异常:65
更新异常:66
更新异常:67
更新异常:68
更新异常:69
更新异常:70
更新异常:71
更新异常:72
更新异常:73
更新异常:74
更新异常:75
更新异常:76
更新异常:77
更新异常:78
更新异常:79
更新异常:80
更新异常:81
更新异常:82
更新异常:83
更新异常:84
更新异常:85
更新异常:86
更新异常:87
更新异常:88
更新异常:89
更新异常:90
更新异常:91
更新异常:92
更新异常:93
更新异常:94
更新异常:95
更新异常:96
更新异常:97
更新异常:98
更新异常:99
更新异常:100

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

4

1 回答 1

-1

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

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

于 2012-11-20T18:35:27.593 回答