Nhibernate 为什么'PreCollectionUpdateEvent'不插入修改过的集合?

Nhibernate 为什么'PreCollectionUpdateEvent'不插入修改过的集合?,nhibernate,Nhibernate,查看了NHibernate.Envers代码后,我意识到我实现了错误的接口。现在我知道了使用什么接口,事情就好一点了 我当前的实现如下所示: public class PreCollectionUpdate : IPreCollectionUpdateEventListener { public void OnPreUpdateCollection(PreCollectionUpdateEvent @event) { var collectionEntry = @

查看了
NHibernate.Envers
代码后,我意识到我实现了错误的接口。现在我知道了使用什么接口,事情就好一点了

我当前的实现如下所示:

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;
        }
    }
}
通过调试,我可以看到它正在被调用,并正确地修改了我的集合。然而,出于某种原因,它首先使用
AddedAt
AddedBy
的默认值插入我的集合中的项目,然后执行更新以填充所述值

以下是我的测试代码:

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();
}
我还尝试了从
IFlushEntityEventListener
继承,但同样的问题也发生了。我提交时没有将
AddedAt
AddedBy
属性持久化到数据库中。但是,我的对象将发生更改,因此下次使用所述对象时,NHibernate将看到它是脏的,并向数据库发出更新命令。我想要的是在初始提交时提交
AddedAt
AddedBy
属性


如果我不清楚,请告诉我。

我猜你需要这样做,而不仅仅是实体。

我不相信你可以使用PreUpdateCollectionEventListener来完成你想做的事情。PreUpdateEventListener在nhibernate管道中都运行得很晚,因此在事件被抛出时,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
}

另外,我知道我曾经读过Ayende关于这方面的一篇非常好的文章,但现在我找不到了。

PreCollectionUpdateEvent
没有
State
属性。我看到有一个
Snapshot
属性,但不确定如何使用它。正确,但要更新的集合中的实体会这样做。因此,您可能需要从持久性上下文中找到这些实体的状态并更新它们的状态。我不喜欢这种方法的地方是,您需要将
AddedBy
AddedAt
作为
OnSaveOrUpdate
(如果您试图保存空值或暂时值,NHibernate会引发异常)在
OnPreInsert
@Nosila之前运行,这是一个好的观点,但您不能通过在构造函数中初始化TrackableEntity的字段(用于保存)来解决这个问题,并且在更新时,它们已经有值了(假定在钩子运行之前,它将是错误的值)。我知道这是肮脏的。我在当前项目中实际正在做的另一个选择是使用IInterceptor,并在事务级别实际创建审核日志。这对用户更有用。然后,您可以将整个事务保存到某种审核日志中。@Acaraso:这就是您想到的文章:
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
}