让我们假设我有两个线程在具有ReadCommitted隔离级别的特定于线程的TransactionScopes中执行一些面向数据库的代码。但是有一些表应该共享数据(不应该创建重复项)。
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 :(得分:2)
在这种情况下,我会做一次双重检查。
首先检查它不存在,这里不需要交易。
然后开始一个可序列化的交易。
检查它是否仍然存在
如果不存在则添加
提交并关闭交易。
答案 1 :(得分:1)
我通常使用数据库约束解决完全相同的问题,并将整个事务包装在try-catch中,并在catch块中重试。当然,如果由于某种原因无法重启交易,这不是一个有效的解决方案(例如,交易是在您的控制范围之外启动的 - 我不确定您的意思是什么“我必须防止异常情况发生“)。
根据事务通常需要多长时间,您可能需要在重试之前等待一段时间或进行几次重试,但只要并行事务成功完成,您的重试就会成功。
棘手的一点通常是如何确定异常是由特定的约束违规引起的。这通常需要一些经验测试来确定确切的异常类型和异常消息的一些丑陋的字符串匹配。