审核NHibernate中的多对多关系

时间:2010-12-24 15:48:15

标签: c# nhibernate many-to-many audit

我已经使用IPreUpdateEventListenerIPreInsertEventListener实现了侦听器来审计应用程序中表的更改,除了我在连接表中没有其他数据的多对多关系外,一切都有效(即我没有为加入表提供POCO。)

每个可审计对象实现一个IAuditable接口,因此事件侦听器会检查POCO是否为IAuditable类型,如果是,则记录对象的任何更改。查找表实现IAuditableProperty接口,因此如果IAuditable POCO的属性指向查找表,则更改将记录在主POCO的日志中。

所以,问题是,我应该如何确定我正在使用多对多集合并在审计表中记录更改?

编辑:我正在使用NHibernate 2.1.2.4000

//first two checks for LastUpdated and LastUpdatedBy ommitted for brevity
else if (newState[i] is IAuditable)
{
    //Do nothing, these will record themselves separately
}
else if (!(newState[i] is IAuditableProperty) && (newState[i] is IList<object> || newState[i] is ISet))
{
    //Do nothing, this is a collection and individual items will update themselves if they are auditable
    //I believe this is where my many-to-many values are being lost
}
else if (!isUpdateEvent || !Equals(oldState[i], newState[i]))//Record only modified fields when updating
{
    changes.Append(preDatabaseEvent.Persister.PropertyNames[i])
        .Append(": ");
    if (newState[i] is IAuditableProperty)
    {
        //Record changes to values in lookup tables
        if (isUpdateEvent)
        {
            changes.Append(((IAuditableProperty)oldState[i]).AuditPropertyValue)
                 .Append(" => ");
        }
        changes.Append(((IAuditableProperty)newState[i]).AuditPropertyValue);
    }
    else
    {
        //Record changes for primitive values
        if(isUpdateEvent)
        {
            changes.Append(oldState[i])
                .Append(" => ");
        }
        changes.Append(newState[i]);
    }
    changes.AppendLine();
}

2 个答案:

答案 0 :(得分:3)

这不会触发的原因是因为集合没有改变,即它们仍然是之前的ICollection的相同实例,但是集合的内容已经改变。

我自己也在寻找,事件监听器不处理这种情况。这可能已经修复了v3.0(但不要引用我)。有一些非理想的解决方法:

1)在对象上放置一个属性,该对象构成集合的字符串表示,以便进行审计。

2)使集合中的项目实现接口,以便单独审核它们。

编辑:还有第三种选择:

“而不是多对多,我有多对一的连接表,然后一对多从它进入属性表。我隐藏加入表POCO背后多对多连接的每个末端的逻辑,但仍然必须实现对象及其上的所有接口。“

答案 1 :(得分:3)

事实证明,实际上有一种方法可以通过事件监听器执行此操作,而无需公开连接表。您只需要使您的事件侦听器实现IPostCollectionRecreateEventListener或IPreCollectionRecreateEventListener。根据我的测试,每当刷新会话时,这些事件都会被激活。这是PostRecreateCollection方法的事件监听器代码。

public void OnPostRecreateCollection(PostCollectionRecreateEvent @event)
        {
            var session = @event.Session.GetSession(EntityMode.Poco);
            var propertyBeingUpdated = @event.Session.PersistenceContext.GetCollectionEntry(@event.Collection).CurrentPersister.CollectionMetadata.Role;

            var newCollectionString = @event.Collection.ToString();
            var oldCollection = (@event.Collection.StoredSnapshot as IList<object>).Select(o => o.ToString()).ToList();
            var oldCollectionString = string.Join(", ",oldCollection.ToArray());

            if (newCollectionString == oldCollectionString || (string.IsNullOrEmpty(newCollectionString) && string.IsNullOrEmpty(oldCollectionString)))
                return;

            User currentUser = GetLoggedInUser(session);
            session.Save(new Audit
            {
                EntityName = @event.AffectedOwnerOrNull.GetType().Name,
                EntityId = (int)@event.AffectedOwnerIdOrNull,
                PropertyName = propertyBeingUpdated,
                AuditType = "Collection Modified",
                EventDate = DateTime.Now,
                NewValue = newCollectionString,
                OldValue = oldCollectionString,
                AuditedBy = Environment.UserName,
                User = currentUser
            });
        }

最棘手的部分是获取正在更新的集合的名称。您必须通过PersistenceContext进行链接以获取集合的Persister,这样您就可以访问它的元数据。

因为没有记录这些事件或监听器,所以我不知道除了flush之外是否会在其他情况下抛出此事件,因此可能会创建错误的审计条目。我计划在该领域做进一步的研究。