NHibernate中会话的乐观并发性

时间:2013-11-15 11:40:02

标签: nhibernate concurrency optimistic-locking

我在NHibernate中以有意义的方式在Web应用程序中实现optimisitc并发性时遇到了麻烦。这是期望的场景:

  1. 用户A打开表单以编辑记录
  2. 用户B打开相同的表单以编辑同一记录
  3. 用户A保存其数据
  4. 用户B尝试保存其数据,但会收到数据已更新的警告。
  5. 常见情况。这是更新代码和实体映射文件:

    <hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" namespace="Entities" assembly="Domain">
        <class name="Record" />
            <id name="Id" column="Id" type="long">
                <generator class="identity"/>
            </id>
            <version name="Version" column="Version" type="datetime" />
            <property name="Description" column="`Description`" type="string" />
        </class>
    </hibernate-mapping>
    
    public void Edit(int id, string description, DateTime version)
    {
        using (var session = sessionFactory.OpenSession())
        using (var tx = session.BeginTransaction())
        {
      var record = session.Get<Record>(id);
      record.Version = version;
            record.Description = description;
            session.Update(record);
      tx.Commit();
        }
    }
    

    当用户打开表单并存储在隐藏字段中时,将加载版本值。我希望NHibernate会尝试使用WHERE子句中的表单版本进行更新,而是使用它刚刚在会话中加载的值。

    我读过的其他内容告诉我,我应该手动比较值,并且(例如)如果它加载了更新版本则抛出异常。但我无法相信有更好的方法。

    在我看来,NHibernate的并发控制只在同一个会话中有用,因此在基于Web的应用程序上陈旧的表单数据时完全没用。

    我希望能够在用户最初加载时根据表单中的内容手动设置版本。这可能吗?或者我在这里遗漏了什么?

2 个答案:

答案 0 :(得分:0)

答案在这里:

  

我希望NHibernate会尝试使用WHERE子句中的表单版本进行更新,而是使用它刚刚在会话中加载的值。

NHibernate会完全/只是我们明确要求的。换句话说,NHibernate如何知道表格中的隐藏字段?网络表格?胜利形式? ...

所以,诀窍是,我们必须设置返回从客户端发送的值,并绑定它到版本。只是一个例子。我更喜欢时间戳,自动生成的byte[]值。因此,虽然我的映射和属性看起来像这样:

<version name="Timestamp" generated="always" unsaved-value="null" type="BinaryBlob">
  <column name="RowVersion" not-null="false" sql-type="timestamp"/>
</version>

这些是两个C#对象属性:

protected virtual byte[] Timestamp { get; set; }
public virtual string Version
{
    get { return Timestamp.IsEmpty() ? null : Convert.ToBase64String(Timestamp); }
    set { Timestamp = value.IsEmpty() ? null : Convert.FromBase64String(value); }
}

因此,现在,客户端“隐藏”字段可以包含Version属性字符串值。一旦发送回服务器/数据层:

  1. NHibernate将要编辑的对象加载到Session。 此时,其Version / Timestamps确实反映了来自DB的加载状态。所以:
  2. 我们必须绑定来自客户端(隐藏字段)的值 ..并从数据库中替换刚刚加载的值。
  3. 现在一切正常,完全符合预期......即使在多用户环境(网络)

答案 1 :(得分:0)

我知道这是一个老问题,但我会在这里留下我惯常的做法。

这是一个知道问题&#34;使用ORM时。 NHibernate和实体框架都遇到了这个&#34;&#34;&#34;,并且它发生是因为ORM在内部跟踪版本值,而不是使用属性返回的版本值。与EF不同,您可以使用byte[]复制Array.Copy值,在NHibernate中,我通常从会话中逐出实体,然后进行更新,这表明您正在更新现有实体但是他正在更新现有实体将使用您刚刚分配的版本开始跟踪。

以下是您的代码片段:

public void Edit(int id, string description, DateTime version)
{
    using (var session = sessionFactory.OpenSession())
    using (var tx = session.BeginTransaction())
    {
        var record = session.Get<Record>(id);
        record.Version = version;
        record.Description = description;

        session.Evict(record);  //  evict the object from the session
        session.Update(record); //  NHibernate will attach the object, and will use your version

        tx.Commit();
    }
}

如果您使用我在模型类中常用的界面(example),您可以轻松创建一些扩展方法,使人们更难忘记。

据我所知,我还没有发现这种方法存在任何问题,但如果您发现任何问题,请告诉我。