2012-01-30 48 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

相关问题