【问题标题】:Audit many-to-many relationship in NHibernate审计 NHibernate 中的多对多关系
【发布时间】:2011-05-30 10:53:16
【问题描述】:

我已经使用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();
}

【问题讨论】:

  • 深入研究这一点,当我修改多对多集合时,似乎我的OnPreUpdateOnPreInsert 事件甚至都没有触发,但是更改正在持久化到数据库中。由于 NHibernate 的一些更深层次的魔力,这可能是预期的行为,但对于未洗过的大众来说,这感觉就像是一个错误/遗漏......

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


【解决方案1】:

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

我自己找过这个,事件监听器不处理这种情况。这可能已针对 v3.0 修复(但不要引用我的话)。有几个不理想的解决方法:

1) 在对象上放置一个属性,该属性使集合的字符串表示形式用于审计。

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

编辑:还有第三个选项:

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

【讨论】:

  • 我最终使用了第三个选项 - 为连接表创建一个显式对象。我不是多对多,而是多对一进入连接表,然后一对多从连接表进入属性表。我将连接表 POCO 隐藏在多对多连接的每一端的逻辑后面,但仍然必须实现对象及其上的所有接口。如果这有意义,您可能希望将其添加到您的答案中(这样比在评论中更容易阅读)
【解决方案2】:

事实证明,实际上有一种方法可以通过事件侦听器执行此操作,而无需公开连接表。您只需让您的事件侦听器实现 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,这使你可以访问它的元数据。

由于没有记录这些事件或侦听器,我不知道除了刷新之外是否会在其他情况下引发此事件,因此它可能会创建错误的审计条目。我计划在该领域做一些进一步的研究。

【讨论】:

  • “我计划在该领域做一些进一步的研究。” - 对此有何更新?
  • 我实际上是在不久前使用此代码完成了该项目。但是我们使用了几个月,没有任何明显的问题。显然没有保证
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-02-08
  • 2012-02-24
  • 2011-03-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-23
相关资源
最近更新 更多