2010-07-21 4 views
3

우리는 NHibernate 이벤트 리스너를 통해 감사 시스템을 구현했다. 청취자는 모든 변경 사항을 추적하여 감사 테이블에 기록합니다. 성능을 극대화하기 위해 우리는 가능한 한 업데이트를 배치 할 수 있도록 감사 테이블에 Guid를 사용했습니다.NHibernate 자식 세션이 제대로 플러시되지 않는다

우리는 다음과 같이 얻을 "자식 세션"에 업데이트를 작성하십시오 NHibernate에 문서에서

protected ISession GetSession(AbstractEvent @event) 
{ 
    if (@event == null) 
    { 
     throw new ArgumentNullException("event"); 
    } 

     ISession childSession = @event.Session.GetSession(EntityMode.Poco); 

     return childSession; 
} 

,이 세션은 그것의의 모든 속성을 상속하는 "아이"세션해야한다 부모 - 거래를 포함하여.

childSession.Save(auditLogEntry); 

이 모든

가 트랜잭션 내에서 호출되고 난 childSession에 대한 변경 사항이 트랜잭션 번 세척 것이라고 기대 : 우리는 엔티티를 생성하면

, 우리는 함께 세션에 저장 최선을 다합니다. 불행히도, 아무 일도 일어나지 않고 변화가 지나치지 않습니다.

저장 직후 수동 플러시와 함께 작동하도록 설정할 수는 있지만 변경 사항이 더 이상 허용되지 않는 성능을 나타내므로 변경되지 않습니다.

처음에는이 동작이 이벤트에만 국한된 것으로 생각했지만 동작을 복제하기 위해 단위 테스트로 추상화 할 수있었습니다.

public void When_Saving_Audit_Log_Records_To_Child_Session_Flushes_When_Transaction_Committed() 
    { 
     ISession session = GetSession(); 
     session.FlushMode = FlushMode.Commit; 

     ITransaction transaction = session.BeginTransaction(); 

     ISession childSession = session.GetSession(EntityMode.Poco); 

     AuditLogEntry entry = CreateAuditLogEntry(); 
     entry.AddAuditLogEntryDetail(CreateAuditLogEntryDetail()); 
     entry.AddAuditLogEntryDetail(CreateAuditLogEntryDetail()); 

     childSession.Save(entry); 
     transaction.Commit(); 
    } 

protected ISession GetSession() 
    { 
     return _sessionFactory.OpenSession(); 
    } 

나는이 밀 NHibernate에 질문의 러닝 아니라는 것을 알고 있지만, 사람이 공유하는 경험이나 조언이 있다면, 나는 그것을 듣고 싶어요.

감사 레코드를 대기열에 기록한 직후 2 초 전이지만 포기하기 전에 모든 가능성을 피하려고했습니다. 사전에

감사합니다,

스티브

답변

0

문제는 FlushMode.Commit에서이다 :이 모드에서, NHibernate에 만 트랜잭션을 커밋에 한 번 세션을 플래시합니다. 따라서 플러시 후 다른 시간을 플러시하지 않으며 플러시 후 변경 사항이 플러시되지 않습니다.

이 문제를 해결하려면 세션을 수동으로 플러시하거나 FlushMode.Auto으로 변경하십시오. 그러나 Auto를 사용하면 자동으로 NHibernate가 질의 전에 플러시되어 결과적으로 OnFlushDirty을 호출하기 때문에 이벤트 리스너 및/또는 인터셉터로 StackOverflowException을주의해야합니다. 따라서 OnFlushDirty에서 무언가를 쿼리하면 다른 플러시가 트리거됩니다. 그런 다음 결코 끝나지 않는 루프에서 OnFlushDirty으로 다시 전화하십시오. 이 상황을 방지하려면 일시적으로 FlushMode를 Never으로 변경하거나 동일한 변경 사항을 반복해서 처리하지 않도록 처리 된 변경 사항을 확인하는 시스템을 구현해야합니다.

0

GUID PK를 사용하여 동일한 방식으로 감사를 처리합니다. Identity PK 생성기를 사용할 때마다 Save를 호출하면 즉시 INSERT가 실행되지만 GUID가 사용되면 INSERT는 플러시 중에 만 실행됩니다. 우리는 NHibernate 소스에 패치를 적용 해 수 년전에이를 해결했습니다. Flush 메서드의 SessionImpl.cs (1467 행)에서 다음을 추가했습니다.

// Flush children when parent is flushed. 
if (childSessionsByEntityMode != null) { 
    foreach (var childSession in childSessionsByEntityMode) { 
     childSession.Value.Flush(); 
    } 
} 
관련 문제