2012-01-30 2 views
3

NHibernate.Envers 코드를 살펴본 후에 잘못된 인터페이스를 구현하고 있음을 깨달았습니다. 이제는 어떤 인터페이스를 사용하여 조금 더 잘 작동하는지 알았습니다.`PreCollectionUpdateEvent`가 수정 된 컬렉션을 삽입하지 않는 이유는 무엇입니까?

내 현재의 구현은 다음과 같습니다 : 디버깅을 통해

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; 
     } 
    } 
} 

나는 그것이 내 컬렉션을 수정 제대로 불러되고 볼 수 있습니다. 그러나 어떤 이유로 먼저 AddedAtAddedBy에 대한 기본값으로 내 컬렉션에 항목을 삽입 한 다음 해당 값을 채우기 위해 나중에 업데이트를 수행합니다.

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(); 
} 

이유입니다 : 여기

내 테스트 코드인가?

내 테스트 디버깅 내 거래를 완료 한 후 AddedByAddedWhen 속성이 모두 올바르게 채워지는 것을 확인할 수 있습니다. 왜 수정 된 컬렉션을 커밋하지 않는지 확실하지 않습니다.

내 코드 전체에 Console.Write 문장을 추가하면 이후에 내 이벤트 수신기가 으로 호출됨을 알 수 있습니다. 내 세션을 커밋합니다.

NHibernate: INSERT INTO Locates (AddedAt, AddedBy, TicketNumber, Status, SomeOtherField) VALUES (@p0, @p1, @p2, @p3, @p4); select last_insert_rowid();@p0 = 1/1/0001 00:00:00 [Type: DateTime (0)], @p1 = NULL [Type: String (0)], @p2 = 123456789 [Type: Int64 (0)], @p3 = 'InProgress' [Type: String (0)], @p4 = NULL [Type: String (0)] 
Saving review... 
Commiting... 
NHibernate: INSERT INTO "Review" (AddedAt, AddedBy, Locate_id) VALUES (@p0, @p1, @p2); select last_insert_rowid();@p0 = 1/1/0001 00:00:00 [Type: DateTime (0)], @p1 = NULL [Type: String (0)], @p2 = NULL [Type: Int32 (0)] 
In event listener... 
Completed filling auditable properties. 

또한 IFlushEntityEventListener에서 inheritign 시도했지만 같은 문제가 발생합니다. AddedAtAddedBy 속성이 데이터베이스에 유지되지 않고 커밋됩니다. 내 객체는 다음 번에 변경 될 것이므로 객체와 함께 NHibernate가 더티라는 것을 알게되고 데이터베이스에 업데이트 명령을 내린다. 내가 원하는 것은 AddedAtAddedBy 속성이 초기 커밋에서 커밋되는 것입니다.

제가 명확하지 않은 경우 알려주십시오.

답변

2

나는 당신이하고 싶은 것을 위해 PreUpdateCollectionEventListener를 사용할 수 있다고 생각하지 않는다. PreUpdateEventListeners는 모두 nhibernate pipline에서 매우 늦게 실행됩니다. 따라서 이벤트가 발생 된 시점에서 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 
} 

PS. 나는 Ayende가 정말 좋은 게시물을 읽은 것을 알고 있지만 지금은 찾을 수 없습니다.

+0

이 접근법에 대해 싫어했던 점은'AddedBy'와'AddedAt'에 nullables를 허용해야한다는 것입니다. (OnSaveOrUpdate'는 null 또는 일시적인 값을 저장하려고하면 Hibernate가 예외를 던지는 곳입니다.) 실행됩니다. 'OnPreInsert' 전에. – Nosila

+0

@ 노질라, 좋은 지적이지만, 저장을 위해 생성자에서 TrackableEntity의 필드를 초기화하고 (이미 저장 한 값을 가질 수있는 업데이트에 대해) 몇 가지 방법으로는 해결할 수 없었습니다. 후크가 실행될 때까지 잘못된 값). 나는 그 더러운 것을 안다. 현재 프로젝트에서 실제로하고있는 다른 옵션은 IInterceptor를 사용하고 실제로 트랜잭션 수준에서 감사 로그를 만드는 것입니다. 이것은 어쨌든 사용자에게 더 유용합니다. 그런 다음 전체 트랜잭션을 일종의 감사 로그에 저장할 수 있습니다. – acarasso

+1

@acarasso : 생각해 본 기사입니다. http://ayende.com/blog/3987/nhibernate-ipreupdateeventlistener-ipreinserteventlistener – DanP

1

내 생각 엔 엔터티뿐만 아니라 modify the state as well이 필요하다고 생각합니다.

+0

'PreCollectionUpdateEvent'에는 'State' 속성이 없습니다. 'Snapshot' 속성이 있지만 사용 방법을 모르겠습니다. – Nosila

+0

이 맞지만 업데이트중인 컬렉션 내의 엔티티가 수행합니다. 따라서 퍼시스턴스 컨텍스트에서 해당 엔티티의 상태를 찾아서 상태를 업데이트해야 할 가능성이 높습니다. –

관련 문제