2013-11-15 46 views
0

我在Web應用程序中以有意義的方式實現NHibernate中的optimisitc併發時遇到問題。下面是不同的場景:NHibernate中會話中的樂觀併發性

  1. 用戶A打開一個表單來編輯記錄
  2. 用戶B打開同一表單編輯同一記錄
  3. 用戶A可以節省他們的數據
  4. 用戶B試圖保存他們的數據,但得到數據已更新的警告。

一種常見的情況。這裏是更新代碼和實體映射文件:

<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的應用程序的陳舊錶單數據時完全沒有用處。

我希望能夠根據用戶最初加載時的形式手動設置版本。這可能嗎?或者我在這裏錯過了什麼?

回答

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. 我們必須綁定來自客戶端(隱藏字段)的值,並從DB中替換新鮮/剛加載的值。

而現在所有的工作,正如預期......即使是在多用戶環境(網絡)

0

我知道這是一個老問題,但我會離開這裏我通常的做法。

使用ORM時,這是一個已知的「問題」。 NHibernate和Entity Framework都會遇到這個「問題」,這是因爲ORM在內部跟蹤版本值,而不是使用屬性返回的值。與EF不同,在EF中,可以使用Array.Copy複製byte[]值,在NHibernate I通常從會話驅逐實體,然後進行更新,這表明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)中使用的接口,您可以輕鬆地創建s ome擴展方法,讓人難以忘記。

據我所知,我沒有發現這種方法的任何問題,但讓我知道,如果你發現任何。