2011-02-14 77 views
3

让我们想象我有两个线程,它们在具有ReadCommitted隔离级别的特定于线程的TransactionScopes中执行一些面向数据库的代码。但是有一些表格应该共享数据(不应该创建重复数据)。TransactionScope细微差别

using (var transactionScope = new TransactionScope(IsolationLevel.ReadCommitted)) 
{ 
    ...//some code 
    if (!_someRepository.IsValueExists(value)) 
     _someRepository.AddData(value); 
    ...//some code 
    transactionScope.Complete(); 
} 

的问题是两个线程可以检查是否在差不多同一时间存在的数据,如果没有 - 创建复制数据(约束不会在这里帮助:我有防止意外情况发生)。我想这是一个微不足道的问题,但它通常如何解决?

我看到下面的示意性的解决方案:

using (var transactionScope = new TransactionScope(IsolationLevel.ReadCommitted)) 
{ 
    ...//some code 
    transactionScope.IsolationLevel = IsolationLevel.ReadUncommitted; //change Isolation Level 
    lock (_sharedDataLockObject) 
    { 
     if (!_someRepository.IsValueExists(value)) 
     _someRepository.AddData(value); 
    } 
    transactionScope.IsolationLevel = IsolationLevel.ReadCommitted; //reset IsolationLevel 
    ...//some code 
    transactionScope.Complete(); 
} 

这种解决方案的第一个问题是,TransactionScope的不支持的IsolationLevel修改。但让我们想象我在这里使用ADO.NET事务。不过,我不确定它是否有效。

+0

`快照`隔离级别是否有助于您的情况?这个级别减少了复制修改行时的锁定。这样,其他事务仍然可以读取旧数据,而无需等待解锁。 – whyleee 2011-02-14 20:25:15

+0

@whyleee:但我的问题是添加记录,但没有修改 – SiberianGuy 2011-02-14 20:37:18

+0

但是,如果你需要一个锁,`ReadCommitted`隔离级锁定来自其他事务的写锁的记录。 – whyleee 2011-02-14 20:44:56

回答

1

我通常解决与DB约束完全相同的问题,并通过在catch块中重试try-catch包装整个事务。当然,如果由于某种原因无法重新启动交易,这不是一个有效的解决方案(例如,交易是在您的控制之外开始的 - 我不确定您的意思,“我必须防止发生异常情况“)。

取决于交易通常需要多长时间,您可能需要稍等一会才能重试或进行几次重试,但只要并行事务成功完成,您的重试将成功。

棘手的问题通常是如何确定异常是由特定的约束违规引起的。这通常需要一些经验性测试来确定确切的异常类型和异常消息的一些丑陋的字符串匹配。

2

在这种情况下,我会做一个双重检查。

首先检查它不存在,不需要在这里进行交易。

然后开始一个可序列化的事务。

检查,它仍然如果不存在添加

提交并关闭交易不存在