2010-12-24 22 views
6

Mam zaimplementowane detektory do audytu zmian w tabelach w mojej aplikacji przy użyciu IPreUpdateEventListener i IPreInsertEventListener i wszystko działa z wyjątkiem relacji wiele do wielu, które nie mają dodatkowych danych w łączeniu tabela (tj. nie mam POCO dla tabeli łączenia).Audit relacji wiele do wielu w NHibernate

Każdy auditowalny obiekt implementuje interfejs IAuditable, więc detektor zdarzeń sprawdza, czy POCO jest typu IAuditable, i czy jest to rejestracja jakichkolwiek zmian w obiekcie. Tabele wyszukiwania implementują inteface IAuditableProperty, więc jeśli właściwość IAuditable POCO wskazuje na tabelę odnośników, zmiany są rejestrowane w dzienniku głównego POCO.

Pytanie brzmi: w jaki sposób określić, czy pracuję z kolekcją wielu do wielu i rejestrować zmiany w mojej tabeli kontroli?

Edycja: używam 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(); 
} 
+0

Kopiowanie do tego, wygląda na to, że moje zdarzenia 'OnPreUpdate' i' OnPreInsert' nawet nie uruchamiają się, gdy modyfikuję zbiory wielu do wielu, ale zmiany są zachowywane w bazie danych. Jest to prawdopodobnie oczekiwane zachowanie ze względu na nieco głębszą magię NHibernate, ale wydaje się, że to błąd/rządy dla nieumytych mas ... – Kendrick

Odpowiedz

3

tego powodu, że nie będzie to ogień, ponieważ zbiory nie uległy zmianie, to znaczy, że wciąż ta sama instancja kolekcji ICollection, że był tam wcześniej jednak zmieniła się zawartość zbiorów.

Sam tego szukałem, a słuchacze wydarzeń nie radzą sobie z tą sytuacją. Mogło to zostać naprawione w wersji 3.0 (ale nie cytuj mnie). Istnieje kilka nieidealnych rozwiązań:

1) Umieść właściwość obiektu, który tworzy ciąg kolekcji dla celów audytu.

2) Utwórz elementy w kolekcji, aby zaimplementować interfejs, aby były one indywidualnie kontrolowane.

Edit: Jest jeszcze trzecia opcja:

„Zamiast wiele-do-wielu, mam wiele-do-jednego, przechodząc do tabeli łączącej, a potem jeden-do-wielu pochodzących z do tabeli właściwości Ukryjmy tabelę łączenia POCO za logiką każdego z końców sprzężenia wielu do wielu, ale wciąż musimy zaimplementować obiekt i wszystkie interfejsy na nim. "

+0

Skończyło się na użyciu trzeciej opcji - utwórz jawny obiekt dla dołączanej tabeli. Zamiast wielu-do-wielu, mam wiele do jednego, idąc do stołu, a potem jeden do wielu, wchodząc z niego do tabeli właściwości. Ukrywam tabelę łączenia POCO za logiką każdego z końców sprzężenia wielu do wielu, ale wciąż muszę implementować obiekt i wszystkie interfejsy na nim. Jeśli ma to sens, możesz dodać go do swojej odpowiedzi (aby łatwiej było go przeczytać niż w komentarzu). – Kendrick

3

Okazuje się, że istnieje sposób, aby to zrobić za pomocą detektorów zdarzeń bez konieczności odsłonięcia tabel łączenia. Trzeba po prostu sprawić, aby detektor zdarzeń implementował IPostCollectionRecreateEventListener lub IPreCollectionRecreateEventListener. Na podstawie moich testów te zdarzenia są wywoływane w celu zmiany kolekcji, gdy sesja jest opróżniana. Oto mój kod nasłuchiwania zdarzeń dla metody 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 
      }); 
     } 

Najtrudniejszą częścią jest aktualizowanie nazwy kolekcji. Musisz przejść przez PersistenceContext, aby uzyskać Persister do kolekcji, która daje dostęp do jego metadanych.

Ponieważ żadne z tych zdarzeń ani detektorów nie są udokumentowane, nie wiem, czy to zdarzenie zostanie rzucone w innych sytuacjach poza opróżnieniem, więc istnieje możliwość utworzenia fałszywych wpisów inspekcji. Planuję zrobić dalsze badania w tej dziedzinie.

+0

"Planuję zrobić dalsze badania w tej dziedzinie." - jakiejkolwiek aktualizacji do tego? – SamuelKDavis

+0

Rzeczywiście zrzuciłem projekt za pomocą tego kodu jakiś czas temu. Ale używaliśmy go przez kilka miesięcy bez żadnych oczywistych problemów. Bez gwarancji oczywiście – AndrewSwerlick

Powiązane problemy