2012-01-30 86 views
3

查看了NHibernate.Envers代碼後,我意識到我實現了錯誤的界面。現在我知道使用什麼界面的工作會更好一些。爲什麼`PreCollectionUpdateEvent`不能插入修改後的集合?

我當前的實現看起來是這樣的:

public class PreCollectionUpdate : IPreCollectionUpdateEventListener 
{ 
    public void OnPreUpdateCollection(PreCollectionUpdateEvent @event) 
    { 
     var collectionEntry = @event.Session.PersistenceContext.GetCollectionEntry(@event.Collection); 

     if(!collectionEntry.LoadedPersister.IsInverse) 
      return; 

     var collection = @event.Collection; 
     var collectionEntries = collection.Entries(collectionEntry.LoadedPersister); 

     foreach(var entry in collectionEntries) 
     { 
      if(!(entry is TrackableEntity)) 
       return; 

      var trackableEntity = entry as TrackableEntity; 
      trackableEntity.AddedAt = Time.Now; 
      trackableEntity.AddedBy = User.Current; 
     } 
    } 
} 

通過調試,我可以看到它被稱爲妥善修改我的收藏。但是,由於某些原因,它首先將項目插入我的收藏夾中,並使用AddedAtAddedBy的默認值,然後稍後執行更新以填充所述值。

這裏是我的測試代碼:

using (var transaction = Session.BeginTransaction()) 
{ 
    var locate = new Locate 
        { 
         TicketNumber = 123456789, 
         Status = Status.InProgress 
        }; 
    Session.Save(locate); 
    transaction.Commit(); 
} 

using (var transaction = Session.BeginTransaction()) 
{ 
    var locate1 = Session.Get<Locate>(1); 
    locate1.AddReview(new AllClear()); 
    Session.Save(locate1); 
    transaction.Commit(); 
} 

using (var transaction = Session.BeginTransaction()) 
{ 
    var locate1 = Session.Load<Locate>(1); 
    transaction.Commit(); 
} 

這是爲什麼?

調試我的測試我可以看到,在提交我的事務後,AddedByAddedWhen屬性都被正確填充。只是不確定爲什麼它沒有提交修改後的集合。

在我的代碼中添加一堆Console.Write聲明我可以看到我的事件偵聽器在之後被調用我提交了會話。

NHibernate: INSERT INTO Locates (AddedAt, AddedBy, TicketNumber, Status, SomeOtherField) VALUES (@p0, @p1, @p2, @p3, @p4); select last_insert_rowid();@p0 = 1/1/0001 00:00:00 [Type: DateTime (0)], @p1 = NULL [Type: String (0)], @p2 = 123456789 [Type: Int64 (0)], @p3 = 'InProgress' [Type: String (0)], @p4 = NULL [Type: String (0)] 
Saving review... 
Commiting... 
NHibernate: INSERT INTO "Review" (AddedAt, AddedBy, Locate_id) VALUES (@p0, @p1, @p2); select last_insert_rowid();@p0 = 1/1/0001 00:00:00 [Type: DateTime (0)], @p1 = NULL [Type: String (0)], @p2 = NULL [Type: Int32 (0)] 
In event listener... 
Completed filling auditable properties. 

我也從IFlushEntityEventListener試圖inheritign但同樣的問題發生。我承諾沒有AddedAtAddedBy屬性被持久化到數據庫。然而,我的對象會改變,所以下次我使用這個對象時,NHibernate會看到它很髒,並向數據庫發出更新命令。我想要的是在初始提交時提交AddedAtAddedBy屬性。

如果我不清楚,請讓我知道。

回答

2

我不相信你可以使用PreUpdateCollectionEventListener來執行你想做的事情。 PreUpdateEventListeners在nhibernate pipline中運行得很晚因此,在引發事件的時候,NHibernate已經將實體的狀態分散到內存中。您需要更改狀態和實體本身,但是在PreUpdateCollectionEvent中,您無法訪問狀態,只要我可以告訴我已經完成了對常規PreUpdateEventListener的操作,例如像這樣應該可以工作:

public class AuditEventListener : IPreUpdateEventListener 
{ 
    public bool OnPreUpdate(PreUpdateEvent @event) 
    { 
      // Grab the entity from the event 
    var trackableEntity = @event.Entity as TrackableEntity; 
    if (trackableEntity == null) 
     return false; 
      // Set the state for the entity (needed to write new values to the db) 
    Set(@event.Persister, @event.State, "AddedAt", time); 
    Set(@event.Persister, @event.State, "AddedByBy", name); 

      // Set the entity values (needed to keep your session in sync) 
    trackableEntity.AddedAt = Time.Now; 
      trackableEntity.AddedBy = User.Current; 

    return false; 
    } 
    private void Set(IEntityPersister persister, object[] state, string propertyName, object value) 
{ 
    var index = Array.IndexOf(persister.PropertyNames, propertyName); 
    if (index == -1) 
     return; 
    state[index] = value 
} 

PS。我知道我曾經閱讀過Ayende關於此事的一篇非常好的帖子,但現在我找不到它了。

+0

我對這種方法不喜歡的是,你需要爲'AddedBy'和'AddedAt'作爲'OnSaveOrUpdate'(這是NHibernate在嘗試保存null或瞬態值時拋出一個異常的地方)運行的允許值在'OnPreInsert'之前。 – Nosila 2012-02-03 16:21:42

+0

@Nosila,這是一個很好的觀點,但是你不能通過在構造函數中初始化TrackableEntity的字段(爲了保存)和更新它們已經有的值來解決這個問題錯誤的值直到鉤子運行)。我知道它很髒。我在當前項目中實際做的另一個選擇是使用IInterceptor,並在事務級別實際創建您的審計日誌。無論如何,這往往對用戶更有用。然後,您可以將整個事務保存到某種審計日誌中。 – acarasso 2012-02-03 17:12:01

+1

@acarasso:這是你正在考慮的文章:http://ayende.com/blog/3987/nhibernate-ipreupdateeventlistener-ipreinserteventlistener – DanP 2012-02-03 19:48:29

1

我的猜測是你需要modify the state as well,而不僅僅是實體。

+0

'PreCollectionUpdateEvent'沒有'State'屬性。我看到有一個「快照」屬性,但不知道如何使用它。 – Nosila 2012-01-30 18:50:07

+0

是正確的,但是您正在更新的集合中的實體可以執行此操作。因此,您可能需要從持久化上下文中找到這些實體的狀態並更新其狀態。 – 2012-01-30 19:12:32

相關問題