2

(업데이트 아래 참조)유창함 NHibernate에 :와 문제 다 대다 사용자 정의 중개 테이블과의 관계

그래서, 내가 (우리가 전화 할게를 ClassA와 ClassB가) 두 개체가 전시 많은이 (ClassA는 여러 개의 ClassB 객체를 가질 수 있으며 그 반대도 가능함).

전통적인 "many-to-many"보다는 실제로 "관계"자체가 다른 두 클래스 (관계 시작 날짜, 관계 이름 등) 간의 연결에 대한 다른 정보를 가진 객체입니다.

ClassA와 ClassB 사이에 many-to-many로 정의하지 않고, 세 번째 중개 클래스에서 가리키는 두 개의 일대 다 관계 (ClassA와 ClassB 중 하나)를 정의했습니다 '관계'라고 부름). 구조적으로, 나는이처럼 보이도록 테이블을 기대 :

 ClassA      Relationship  
----------------    ---------------- 
     Id (PK)      Id (PK) 
    Name       Name 
    Description     StartDate 
           ClassA_Id (FK) 
    ClassB      ClassB_Id (FK) 
---------------- 
     Id (PK)  
    Name   
    Description 

는이를 달성하기 위해, 그래서 같이 매핑을 설정 (시행 착오의 후) :

public ClassAMap() 
{ 
    Id(x => x.Id); 
    Map(x => x.Name); 
    HasMany(x => x.Relationship)  
     .KeyColumn("ClassA_Id") 
     .ForeignKeyConstraintName("FK_Relationship_ClassA") 
     .LazyLoad() 
     .Cascade.All() 
     .Inverse(); 
} 

public ClassBMap() 
{ 
    Id(x => x.Id); 
    Map(x => x.Name); 
    HasMany(x => x.Relationship)  
     .KeyColumn("ClassB_Id")   
     .ForeignKeyConstraintName("FK_Relationship_ClassB") 
     .LazyLoad() 
     .Cascade.All() 
     .Inverse(); 
} 

public RelationshipMap() 
{ 
    Id(x => x.Id); 
    Map(x => x.CreatedDate); 
    References(x => x.ClassAObject) 
     .Column("ClassA_Id") 
     .Cascade.SaveUpdate(); 
    References(x => x.ClassBObject) 
     .Column("ClassB_Id") 
     .Cascade.SaveUpdate(); 
} 

이제 상위 개체를 추가/삭제할 때 그의 개체는 예상대로 정확하게 작동합니다. 예를 들어, 새로운 ClassA와 ClassB를 생성하고 관계를 연결 한 다음 ClassA를 저장하면 관련된 모든 레코드가 세 개의 테이블에 추가됩니다. 마찬가지로 동일한 ClassA를 제거하면 ClassA 레코드와 관계 레코드는 제거되지만 ClassB 레코드는 남아 있습니다 (예상되는 동작). 하지만, 난 그냥 부모 개체 중 하나 (예 : classA.Relationships.Remove(relationshipObject); classARepository.Update(classA);)에서 관계를 제거하려고하면

후 나는 다음과 같은 오류를 얻을 : 또한

NHibernate.StaleObjectStateException: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect): [ClassB]

NHibernate.ObjectDeletedException: deleted object would be re-saved by cascade (remove deleted object from associations) [Relationship#9]

, 데이터베이스에서 ClassA 개체를 완전히 다시로드하고 위에서 설명한대로 멤버 자격을 제거하면 오류없이 실행되지만 관계는 데이터베이스에서 제거되지 않습니다.

찾고있는 동작을 달성하기 위해 매핑을 어떻게 변경할 수 있습니까?

  1. 를 ClassA/B/관계가 추가 된
    • 기록 관계 새로운를 ClassA를 추가
  2. 제거 업데이트 : 참고로

    , 여기에 통과해야 시나리오의 개요입니다 기존 ClassA

    • ClassA 및 관계에 대한 레코드가 삭제되었습니다.
    • ClassB 레코드가 테이블에 남아 있습니다.
  3. ClassA에서 관계를 제거하십시오.관계 목록;

    : @ JamieIde의 답변에 따라, 나는 다음과 같은 조정을 내놓았다 : 이
  4. 를 ClassA와 ClassB가 기록

UPDATE 남아 관계 테이블에서를 ClassA

  • 제거 관계를 업데이트
    • 관계에 대한 ClassA 및 ClassB에 대한 참조를 제외합니다.
    Cascade.AllDeleteOrphan()로를 ClassA/ClassB가에
    // NOTE, this is an example snippet to show the steps that need to be taken 
    
    using(var session = SomeSessionFactory().OpenSession()) 
    { 
        using (var transaction = session.BeginTransaction()) 
        { 
         // preferably place these four lines in a public method on one of the objects 
         classAObject.Relationships.Remove(relationship); 
         classBObject.Relationships.Remove(relationship); 
         relationship.ClassA = null; 
         relationship.ClassB = null; 
    
         transaction.Commit(); 
        } 
    } 
    
    • 변경 .Cascade.All(). 이것은 당신은 매핑을 변경할 필요가 없습니다
    public ClassAMap() 
    { 
        Id(x => x.Id); 
        Map(x => x.Name); 
        HasMany(x => x.Relationship)  
         .KeyColumn("ClassA_Id") 
         .ForeignKeyConstraintName("FK_Relationship_ClassA") 
         .LazyLoad() 
         .Cascade.AllDeleteOrphan() // here's the key line 
         .Inverse(); 
    } 
    

답변

1

, 당신에 대한 참조를 null로 필요로하는 관계 객체에 대한 참조를 제로 화시키는 후를 ClassA와 ClassB가 테이블에 빈 레코드를 삽입에서 NHibernate에 방지 관계 개체의 한쪽

classA.Relationships.Remove(relationshipObject); 
relationshipObject.ClassAObject = null; 
session.Flush(); 

은 기본적으로 당신은 메모리 객체 그래프가 정확하고 NHibernate에 의해 지속 할 수 있도록 관계의 양쪽을 유지하기 위해주의해야합니다. 일반적인 방법이를 캡슐화하는 것입니다 또한

void RemoveRelationship(RelationshipObject relationshipObject) 
{ 
    Relationships.Remove(relationshipObject); 
    relationshipObject.ClassAObject = null; 
} 

, 당신이 단지 (더 좋은) 세션을 세척하거나, Update 전화 트랜잭션을 커밋 할 가능성은 적습니다.

+0

+1, 이것은 옳은 길에 나에게 확실히 도착했다. 부모 객체를 null로 만드는 것만으로 NHibernate가 빈을 삽입하도록 만들었 기 때문에 부모 객체의'Cascade.All()'을'Cascade.AllDeleteOrphan()'으로 변경해야했다. ClassA/ClassB 테이블에 레코드가 있습니다. 또한, 명시 적으로 update (나는 NHibertate에 익숙하지 않습니다.)를 호출하지 않고 개체의 변경 사항이 선택된다는 것을 인식하지 못했습니다. 작업 코드로 내 질문을 업데이트 할 것입니다. – valverij