Database Nhibernate IList(有序列表)竞争条件

Database Nhibernate IList(有序列表)竞争条件,database,nhibernate,concurrency,Database,Nhibernate,Concurrency,想象一下这个场景: 实体上的有序集合 两个同时(异步)创建集合中新项的请求 两个新项目的序号相同 收藏是这样的: public virtual IList<CustomField> CustomFields { get; protected set; } 原因是nhibernate正在查询数据库以获取下一个可用序号,而表上没有锁,并且两者都获取相同的序号 如何防止这种情况发生?db中的唯一索引/约束将解决这一问题。当然,您将不得不处理两个请求之一的失败刷新 如果您想避免在应用程序端

想象一下这个场景:

  • 实体上的有序集合
  • 两个同时(异步)创建集合中新项的请求
  • 两个新项目的序号相同
  • 收藏是这样的:

    public virtual IList<CustomField> CustomFields { get; protected set; }
    
    原因是nhibernate正在查询数据库以获取下一个可用序号,而表上没有锁,并且两者都获取相同的序号


    如何防止这种情况发生?

    db中的唯一索引/约束将解决这一问题。当然,您将不得不处理两个请求之一的失败刷新

    如果您想避免在应用程序端处理异常,您可以在读取(或从db刷新)列表状态之前(或者更进一步)对保存列表的实体发出独占db锁(
    ISession.lock(entity,LockMode.Upgrade)
    )。然后,更新列表。
    如果所有插入列表的应用程序都遵循相同的模式,则它们将无法同时插入具有相同索引的元素:它们将等待释放锁,然后在读取新数据并插入自己的新元素之前,将新数据保存在db中。
    这是一个很好的例子。阅读更多(可在此找到链接)


    乐观的方法需要使用行版本列,并确保任何列表更新都会触及实体版本。但在并发的情况下,两个请求中的一个会出现并发异常。

    我最终用Nhibernate的
    锁模式解决了这个问题。升级
    。这非常有效,解决了问题。

    表中的序号不是唯一的,因为一个实体可以有1-3,另一个实体可以有1-3。另外…允许用户对集合进行重新排序…唯一索引/约束当然应该包括实体id。至于重新排序,我希望NHibernate通过以相应的方式发布其更改来处理,但您应该检查一下。(在我的回答中,我已经更改了
    锁定模式
    写入
    在检查文档时看起来并不好。)考虑到我在实际案例中使用的是什么,我不使用
    列表
    映射。我更喜欢使用带有索引属性的映射列表项实体(我的应用程序代码自己处理),并且在集合中有一个映射,通常映射为
    set
    。这使我能够更好地控制如何处理排序,并且对不一致的索引更具容忍度?在这种情况下,您如何处理这样的情况,即您有两个异步请求?我根本不关心(对于我的实际情况):它们将具有相同的索引,并且sql order by可以容忍(我的应用程序代码也是如此),它仍将具有一些合理的顺序。在下一次更新时,它将被重新排序。您能详细介绍一下您的实际用例吗?这样的问题可以(应该?)在数据库级别处理,可能会改变您在创建过程中处理事务的方式。另外,了解您正在使用的数据库可能是一个有用的信息。我正在使用sql server(在azure中)。关于我的用例,您还想知道什么。这和描述的一模一样。你认为这最好在db级别处理?目前,我已经开始工作了……我在nhibernate事务中使用了一个表锁,现在它开始工作了。你写下“两个新项目都有相同的序号。”但这就是效果。这不是“怎么做”。如何让NHibernate生成序号?问题的原因(以及可能的解决方案)就在这一点之内。“使用表锁”是在数据库级别解决此问题的一种方法。另一个是使用默认值配置列,并让db处理计算。fluent nhibernate映射和属性配置。根据用例,您可以使用专用线程同步创建请求。这仍然允许单个工作线程在彼此等待时异步处理它。这种同步行为是使用数据库上的锁时会发生的。相反,您可以在代码中显式地管理它。
    mapping.HasManyToMany(cp => cp.CustomFields)
                .AsList(i => i.Column("CustomFieldOrdinal"))
                .ParentKeyColumn("RegistrationId")
                .ChildKeyColumn("CustomFieldId");