我有这个统计模块代码:
using(TransactionScope scope = new TransactionScope(TransactionScopeOption.Required, new TransactionOptions {IsolationLevel = IsolationLevel.RepeatableRead}))
{
PersonDayStatisticsUnit pdsu = db.PersonDayStatisticsUnits.FirstOrDefault(x => x.UnitId == su.Id && x.Date == now && x.PersonAnonimousGuid == personGuid);
if (pdsu == null)
{
pdsu = new PersonDayStatisticsUnit()
{
UnitId = su.Id,
Count = 1,
Date = now,
PersonAnonimousGuid = personGuid
};
db.PersonDayStatisticsUnits.Add(pdsu);
ret = true;
}
else
{
pdsu.Count++;
ret = false;
}
db.SaveChanges();
scope.Complete();
}
此代码在 ASP.NET MVC 5 上下文中工作。
发现此代码失败,出现无法使用重复键创建记录的错误。
错误是什么,我意识到显然两个线程进入了事务并试图创建同一个对象PersonDayStatisticsUnit
,db.PersonDayStatisticsUnits.Add(pdsu)
.
也就是说,我错过了代码中的某些内容......
据我了解,所有这些代码都可以简单地放在 中lock
,这应该可以解决问题。
这里有必要lock
吗?解决这种情况的最佳方法是什么?事务如何与lock
-s 关联,它们可以或应该同时完成,还是只需要在这里进行不同类型的事务IsolationLevel
?
在那种情况下,我会尝试设置 IsolationLevel Serializable。如果它没有帮助,那么你可能需要一个锁。
设置 IsolationLevel 和 lock(在一般情况下)都无济于事。
如果您只有一个应用程序实例,lock会有所帮助。那些。如果您的应用程序很小并且不打算扩展,这是一个很好的解决方案。
一般来说——如果你有两个或更多的服务器——锁不会有帮助。
IsolationLevel 无济于事,因为它不能按您期望的方式工作。它控制两件事:
如果尝试在资源(表、行、部分)上设置锁,但锁不兼容,则请求会一直等待,直到该特定锁被释放。比如你可以同时下注两个S锁,同时下注S和U,但不能下注两个U。
这在Transaction Locking and Row Versioning Guide中有足够详细和广泛的描述,但简而言之,
因此,在任何级别的事务隔离中,您都可以进行同时提取。
如果您需要禁止同时选择 - 您需要 - 在选择期间设置不是 S-lock,而是 U-lock 或 X-lock - 保持它直到事务结束
可以通过查询文本中的提示更改锁定类型和级别 (TABLE HINT)
那些。您要么在第一个 SELECT 中,要么在它之前,您需要执行 SQL
这将阻止在另一个事务中执行完全相同的 SQL。或者,您可以查找为 EF 准备的内容,例如HintsInterceptor,并将适当的提示附加到第一个查询或整个表。