Аудит отношения многих ко многим в NHibernate - PullRequest
6 голосов
/ 24 декабря 2010

Я внедрил слушателей для аудита изменений таблиц в моем приложении, используя IPreUpdateEventListener и IPreInsertEventListener, и все работает, за исключением моих отношений «многие ко многим», у которых нет дополнительных данных в присоединяющейся таблице (т.е. я не 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 ]

3 голосов
/ 24 августа 2012

Оказывается, что на самом деле есть способ сделать это с помощью прослушивателей событий, не раскрывая соединяющиеся таблицы.Вам просто нужно, чтобы ваш прослушиватель событий реализовал 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, существует вероятность, что он может создать ложные записи аудита.Я планирую провести дальнейшие исследования в этой области.

3 голосов
/ 24 февраля 2011

Причина, по которой это не сработает, заключается в том, что коллекции не изменились, то есть они все еще являются тем же экземпляром ICollection, который был там ранее, однако содержимое коллекций изменилось.

Я искал это сам, и слушатели событий не справляются с этой ситуацией. Возможно, это было исправлено для v3.0 (но не цитируйте меня об этом). Есть несколько неидеальных обходных путей:

1) Поместите в объект свойство, которое создает строковое представление коллекции для целей аудита.

2) Сделайте так, чтобы элементы в коллекции реализовали интерфейс, чтобы они проверялись индивидуально.

Редактировать: есть третий вариант:

"Вместо того, чтобы многие-ко-многим, у меня есть много-к-одному идущий к соединительному столу, а затем один-ко-многим, идущий из него в таблицу свойств. Я скрываю соединительную таблицу POCO позади логика каждого из концов соединений «многие ко многим», но все же необходимо реализовать объект и все интерфейсы на нем ».

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...