2012-02-22 5 views
2

사용자가 Windows Forms 응용 프로그램의 데이터베이스에서 개체를 편집하고 데이터베이스 제약 조건 (예 : 열 고유 값)을 위반하는 편집을 수행하고, 엔티티가 데이터베이스로 되돌아 간다. 그리고 NHibernate는 예외를 던져 세션을 파기한다.NHibernate를 사용하여 Winforms에서 제약 조건 위반 처리

MSDN 문서 Building a Desktop To-Do Application with NHibernate에 게시 된 지침을 사용하고 발표자 당/양식 방식을 사용하고 있습니다. 발표자를 만들 때 SessionFactory에 대한 참조를 유지하고 새 세션을 만들고 세션을 통해 데이터베이스에서 객체를 검색 한 다음 [객체]에 대한 참조를 저장합니다. 폼이 표시되면 해당 필드가 객체에서 채워집니다.

양식을 변경하고 사용자가 데이터를 저장하려면 필드 값에서 개체가 업데이트되고 개체가 데이터베이스에 다시 저장됩니다.

데이터베이스에서 검색 한 시간과 저장 한 시간 사이에 개체가 변경되는 오래된 상태 예외를 처리합니다. 새 세션이 만들어지고 원래 개체가 다시로드되고 사용자에게 갈등이 발생했으며 변경 사항과 현재 데이터베이스에있는 내용이 표시됩니다. 그는 변경 사항을 저장하거나 취소 할 수 있으며 현재 데이터베이스에 저장된 내용을 수락 할 수 있습니다. 제약 조건 위반이 발생할 경우

, 두 가지 중 하나가 일어날 수있는 것으로 보인다 :

  1. 목적은이 새로운 세션으로 다시로드 할 수있는 데이터베이스에 변경되지 않았습니다.
  2. 개체도 데이터베이스에서 변경되었으며 처음로드 된 개체는 더 이상 반영되지 않습니다.

그러나, 나는 내가 실제로 제약 예외가 (내가 이것을 테스트 한) 발생했기 때문에 부실 상태 예외가 발생하지 않기 때문에 발생하는 경우 감지 할 수 있다고 생각하지 않습니다.

케이스 1은 "FIELD-X에 이미 DB에있는 값이 있습니다"라는 오류 메시지를 표시하고 실제로 아무런 문제가없는 것처럼 가장하기 때문에 취급 사례 1은 간단합니다. 사용자는 FIELD-X를 고유 한 값으로 변경하고 변경 사항을 다시 입력 할 필요없이 다시 저장할 수 있습니다.

처리 사례 2는 일반적인 부실 상태 예외를 처리하는 것과 같습니다.

원본 (또는 그 값)의 복사본을 유지 한 다음 두 필드를 비교하여 "무차별 대입"할 수 있음을 알고 있습니다. 그러나 NHibernate를 활용하여이를 처리하는 더 좋은 방법이 있다고 생각합니다. 어떻게 처리할까요?

경우는 도움이 될 것입니다 :

  • 자 NHibernate 버전 : 3.2
  • 낙관적 잠금 사용 "더러운"전략
  • .NET 프레임 워크 2.0 SP2
  • SQLite 데이터베이스/드라이버

2 월 23 일 수정 - 현재 내가하고있는 것을 더 잘 보여주기 위해 몇 가지 예제 코드를 추가했습니다.

/** 
* Save handler called when user initiates save in UI. 
* 
* Returns true when save was successful (essentially, tells the presenter 
* that the UI can be closed. 
*/ 
private bool SaveData() 
{ 
    try 
    { 
     if (this.creatingNewUserAccount) 
     { 
      // Do whatever is necessary to instantiate a new object. 
      this.userAccount = new UserAccount(); 
      // and copy values from the UI into the new object. 
      this.userAccount.Name = this.form.Name; 
      // etc. 
     } 
     else 
     { 
      // Just copy values from the UI into the existing object 
      // from the database. 
      this.userAccount.Name = this.form.Name; 
      // etc. 
     } 

     using (ITransaction tx = this.session.BeginTransaction()) 
     { 
      this.accountRepository.store(this.userAccount); 
      tx.Commit(); 
     } 

     return true; 
    } 
    catch (StaleObjectStateException) 
    { 
     HandleStaleStateException(); 
     return false; 
    } 
    catch (ArgumentException e) 
    { 
     this.m_View.ShowOtherDialog(e.Message); 
     return false; 
    } 
    catch (GenericADOException e) 
    { 
     HandleConstraintViolationException(); 
     return false; 
    } 
} 

private void HandleStaleStateException() 
{ 
    // The session was trashed when the exception was thrown, 
    // so close it and create a new one. 
    this.session.Dispose(); 
    this.session = this.sessionFactory.OpenSession(); 
    CurrentSessionContext.Bind(this.session); 

    // Reload the object from the database. 
    this.userAccount = LoadData(); 

    // Do a bunch of things that deal with informing the user 
    // of the stale-state and displaying a form to merge changes. 
    HandleEditConflict(); 
} 

private void HandleConstraintViolationException() 
{ 
    // The session was trashed when the exception was thrown, 
    // so close it and create a new one. 
    this.session.Dispose(); 
    this.session = this.sessionFactory.OpenSession(); 
    CurrentSessionContext.Bind(this.session); 

    // Determine if trying to save a new entity or editing an existing one. 
    if (this.creatingNewUserAccount) 
    { 
     // If saving a new entity, we don't care about the old object 
     // we created and tried to save. 
     this.userAccount = null; 
    } 
    else 
    { 
     // ???? 
    } 
} 

답변

1

에 무엇에 관심이없는 경우. 내 질문의 코드는 최종 방법을 제외하고는 동일하게 유지됩니다.

0

그럴 수

// after ConstraintException with new session 

session.Lock(loadedObject, LockMode.None); // attach object with session 
// copy back from UI 
session.Flush(); 
catch() 
{ 
    if (ConstraintException) 
     // repeat 
    else if (Stale) 
     // handle like you have 
    else 
     // all ok 
} 

ISession.Refresh(Object obj) 방법은 나를 위해 일하는 결국 무엇 이었는가 DB

// loads from the database, copy state from object into it and returns the loaded object (attached to session), changes will be updated on next flush 
obj = session.Merge(obj); 
+0

내가 의도 한대로 내 코드를 통합했는지는 확실하지 않습니다. 응답 할 때 나는 아무 것도 없었습니다. 코드는 당신을 위해 보냈지 만 - 그러나 이것은 나에게 도움이되지 못했습니다. Lock()은 엔티티를 다시 연결하지 않은 것으로 보이고 Flush()는 DB에 다시 저장하려고 시도합니다. 그러나 Merge()를 사용 하자는 제안은 API를 더 자세히 살펴보고 Refresh()를 찾았습니다. –