2009-11-11 7 views
8

나는 학습 운동으로 블로그 엔진을 쓰고있다. 거기에 블로그 엔진이 많이 있지만, 나와 함께 곰이 ...피하는 방법 NHibernate.NonUniqueObjectException

나는 그것과 관련된 태그의 IList라는 속성을 가진 BlogPost 엔터티를 가지고있다. BlogPost.SetTags (string) 메서드는 문자열을 분할하고 지정된 태그 이름으로 새 Tag 객체를 만든 다음 목록에 추가합니다. BlogPost.AddTag (string tagName)과 동일합니다.

나는 "foo"라는 태그 엔티티가 이미 존재하고 데이터베이스에 유지되는 BlogPost.AddTag ("foo")를 호출 할 때 nHibernate가이를 인식하고 게시물을 와이어 화합니다. 기존 태그와

BlogRepository.Save() 메서드에서 태그 목록의 각 태그가 이미 있는지 확인합니다. 그렇지 않으면 TagRepository.Save (tag) 호출을 사용하여 저장합니다.

문제는 다음 예제 코드에서 "NHibernate.NonUniqueObjectException : 동일한 식별자 값을 가진 다른 개체가 이미 세션 : 태그 1, 엔터티 : CMS.Core와 연결되어 있습니다. Model.Tag "를 사용하여 기존 태그를 사용하여 BlogPost 객체를 유지하려고합니다. 새 태그 만 사용하는 BlogPost 객체를 유지하면 생성되고 모든 것이 정상입니다.

참고 데이터베이스의 기본 키로 TagName을 사용하여 bp_Tags 테이블을 사용하고 있습니다. 테이블에 고유 한 태그 이름 만 저장하는 경우 정수 또는 GUID PK를 사용하는 것은 불필요한 것으로 보입니다.

내 자 NHibernate 구성 보이는 같은

<class name="CMS.Core.Model.Tag,CMS.Core" table="bp_Tags"> 
    <id column="TagName" name="TagName" type="String" unsaved-value=""> 
     <generator class="assigned" /> 
    </id> 
    </class> 

    <class name="CMS.Core.Model.BlogPost,CMS.Core" table="bp_Content"> 
    <id name="Id" column="Id" type="Int32" unsaved-value="0"> 
     <generator class="native"></generator> 
    </id> 
    <property name="SubmittedBy" column="SubmittedBy" type="string" length="256" not-null="true" /> 
    <property name="SubmittedDate" column="SubmittedDate" type="datetime" not-null="true" /> 
    <property name="PublishDate" column="PublishDate" type="datetime" not-null="true" /> 
    ...  
    <bag name="_tagsList" table="bp_Tags_Mappings" lazy="false" cascade="all"> 
     <key column="Target_Id" /> 
     <many-to-many class="CMS.Core.Model.Tag,CMS.Core" column="TagName" lazy="false" /> 
    </bag> 

NHibernate.NonUniqueObjectException : 태그 1 엔티티 : 동일한 식별자 값을 다른 개체가 이미 세션과 연관 Bariliant.CMS.Core.Model. 태그

BlogPost post, post2; 

    using (UnitOfWork.Start()) 
    { 
     post = BlogPostFactory.CreateBlogPost("test post", "test body"); 
     post.Publish(); 
     BlogRepository.Save(post); 
     UnitOfWork.Current.Flush(); 

     post.SetTags("tag 1, tag 2"); 
     BlogRepository.Save(post); 
     UnitOfWork.Current.Flush(); 
    } 

    using (UnitOfWork.Start()) 
    { 
     post2 = BlogPostFactory.CreateBlogPost("test post2", "test body"); 
     post2.Publish(); 
     BlogRepository.Save(post2); 
     UnitOfWork.Current.Flush(); 

     post2.AddTag("tag 1"); 
     BlogRepository.Save(post2); // throws 

... 내가 잘못을하고 해결 방법을하는 무슨을에

어떤 생각?

+2

나는 문제가 내부이며 기존 태그의 목록을 얻을 방법에서 유래 믿습니다

var tag = session.Get<Tag>("Tag 1"); if (tag != null) { post.AddTag(tag); } else { post.AddTag(new Tag("Tag 1")); } 

이 블로그 게시물은 당신에게 자세한 설명을 제공합니다 새 BlogPost 객체와 비교하고 기존 BlogPost 객체에 기존 객체를 할당하는 방법 BlogRepository.Save()의 코드를 게시하는 것이 좋습니다. 이 모든 것이 우리가 문제를 발견 할 수 있도록하기 위해서 발생합니다. – tolism7

답변

8

TagName이 ID이므로 NHibernate의 ID 맵에 대해 실행 중입니다. ID 맵은 이미 동일한 ID를 가진 객체를 인식하고 있으므로 예외가 발생합니다.

해당 태그가 이미 해당 세션에 있는지 확인하고 그럴 경우 해당 기존 태그를 두 번째 게시물과 연관 지어 볼 수 있습니다.

사이비 - 코드 예제 : NHibernate - Cross session operations

+0

감사합니다. 나는 비슷한 일을 끝내었다. 태그를 추가하기 전에 호출자를 확인하거나 컨텐츠 엔티티가 태그 저장소에 의존하여 체크를 수행하는 대신, 컨텐츠 저장소 기본 클래스에서 점검합니다. 태그가 이미있는 경우 '새'태그를 제거하고 '기존'태그를 다시 추가합니다. 매력처럼 작동하고 내 의존성 목표를 깨지 않습니다. –

4

당신이이 일을하는 방식은 내가하는 방식이 아니지만 여기에 문제를 해결하는 방법이 있습니다. 당신은 동일한 상태 2 개 별도의 객체를했습니다

var object1 = new Tag("hello"); 
var object2 = new Tag("hello"); 

var areSame = (object1 == object2); // false 

,하지만 당신은 어떤지를 비교한다면 그들은이 동일하지 그래서 그들은 두 개의 서로 다른 개체 : 일반적으로 객체에 다음을 프로그래밍 지향이 객체는 동일하지 . 분명히 NHibernate에 관해서는이 객체들은 실제로 같은 엔티티이다.

우리는 객체 클래스의 2 가지 메소드를 오버라이드하여 NHibernate를 해결했다. GetHashCode() 및 Equals()

GetHashCode()는 기본적으로 객체의 상태에 따라 고유 한 해시 코드를 반환합니다. 같음()이 두 객체의 동등성이 추천

비교 :

public override int GetHashCode() 
{ 
    return (this.GetType() + "|" + _tagName).GetHashCode(); 
} 

public override bool Equals(object obj) 
{ 
    return this.GetHashCode() == obj.GetHashCode(); 
} 

기본적 GetHashCode 개체 유형 및 App.Domain.Tag|nameoftag 즉 문자열로 태그의 이름을 연결 및 해당 문자열의 해시를 생성

Equals()는 첫 번째 개체의 GetHashCode() 결과와 두 번째 개체의 GetHashCode() 결과를 비교하여 동일성을 테스트합니다. 위에서 정의한 두 객체를 사용하여 이렇게하면 두 개의 해시 코드가 동일하므로 Equals()에 대한 비교가 true가됩니다. NHibernate가 두 가지 객체를 내부 동작에서 평등하게 테스트 할 때 두 객체가 동일하다고 판단하고 문제를 해결해야합니다.

+0

감사합니다. 이러한 재정의가 있었지만 구현이 더 좋고이를 채택했습니다. –

+0

그건 그렇고,이 시나리오를 어떻게 다룰 지에 대한 의견을 듣고 싶습니다. –

+3

@Joe :'GetHashCode'가 다른 인스턴스에 대해 고유 해시 값을 반환하지 않기 때문에'Equals'를 이렇게 구현하는 것은 권장하지 않습니다. 이런 식으로'Equals'는 ** 실제로 ** 같지 않은 객체에 대해 'true'를 반환 할 수 있습니다. 이렇게하면 예외 문제가 해결되지만, 예를 들어 'Hashtable'에서 잘못된 인스턴스가 반환 될 수 있습니다. – Groo

관련 문제