想象一下这个场景:
- 实体上的有序集合
- 对集合中新项的两个同时(异步)CREATE请求
- 两个新项目得到相同的序号
收集是这样的:
public virtual IList<CustomField> CustomFields { get; protected set; }
我有流畅的nhibernate映射,如下所示:
mapping.HasManyToMany(cp => cp.CustomFields)
.AsList(i => i.Column("CustomFieldOrdinal"))
.ParentKeyColumn("RegistrationId")
.ChildKeyColumn("CustomFieldId");
原因是nhibernate正在查询数据库以获取下一个可用的序号,而表上没有锁,并且两者都获得了相同的序号。
如何防止这种情况发生?
db中的唯一索引/约束可以处理这一问题。当然,对于这两个请求中的一个,您将不得不处理一个失败的flush。
如果您想避免在应用程序端处理异常,那么在读取(或从数据库刷新)其列表状态之前,您可以(或另外)对持有列表(ISession.Lock(entity, LockMode.Upgrade)
)的实体发出独占数据库锁。然后,更新列表
如果所有插入列表的应用程序都遵循相同的模式,那么它们将无法同时插入具有相同索引的元素:它们将等待锁释放,然后在读取并插入自己的新元素之前,将新数据保存在db中
这是一种悲观的并发模式。在这里也可以阅读更多信息(在另一个答案上可以找到链接)。
乐观的要求使用行版本列,并确保任何列表更新都与实体版本相接触。但是,在并发的情况下,对于两个请求中的一个,您将有一个并发异常。
我最终用Nhibernate的LockMode.Upgrade
解决了这个问题。这很有效,解决了问题。