如果两个请求在ASP.NET MVC应用程序中并排在一起,则我的竞争条件如下所示:
var workload = org.Workloads.SingleOrDefault(p => ...conditions...);
if (workload == null) {
workload = org.CreateWorkload(id);
}
workload
和org
是EntityFramework对象。对CreateWorkload
的调用在数据库的Workloads表中添加了一行。 (我们确实应该在表上使用UNIQUE约束来强制执行此操作,但现在无法在表中包含一些脏数据。)随后对包含此代码的Action方法的调用在SingleOrDefault
遇到更多错误时会引发异常。多于满足条件的一行。因此,要解决此问题,我想
lock
这几行代码。我不希望每个请求都使用静态锁定对象来完成,因为这会使每个用户的网站速度变慢。我想做的是使用Session.SyncRoot
进行锁定。即Workload workload;
lock (Session.SyncRoot)
{
workload = org.Workloads.SingleOrDefault(p => ...conditions...);
if (workload == null) {
workload = org.CreateWorkload(id);
}
}
我不是ASP.NET专家,但是在文档和ReSharper中显示了一些警告信号,即它可能引发NotImplementedExceptions或为null。但是,测试表明这可以正常工作。
那么,ASP.NET专家为此使用Session.SyncRoot有什么风险?或者,如果Session.SyncRoot具有“真正的风险”,我可以在启动会话时在Session集合中分配一个锁对象来执行相同的操作吗?
最佳答案
仅当您使用实现HttpSessionStateBase
但不重写SyncRoot
属性以执行除抛出NotImplementedException以外的其他操作的自定义会话类时,此危险才存在。 HttpSessionStateWrapper
类和HttpSessionState
类必须实现并重写SyncRoot
方法。因此,只要您是通过HttpSessionStateWrapper
或HttpSessionState
类而不是自定义类访问Session,这将很好地工作。