2011-08-15 2 views
0

AssetGroup 엔티티는 Asset 엔티티와 일대 다 관계를 가지고 있습니다. Equals 및 GetHashCode를 재정의하는 Entity 기본 클래스가 있습니다. 나는 ch 20 parent childNHibernate one-to-many 관계의 문제 캐스케이드를 사용한 콜렉션

<?xml version="1.0" encoding="utf-8" ?> 
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" 
        assembly="TestNHibernate" 
        namespace="TestNHibernate.Models" auto-import="true"> 
    <class name="AssetGroup"> 
    <id name="Id" column="Id" type="guid"> 
     <generator class="guid"></generator> 
    </id> 
    <property name="Name" type="string" not-null="true"/> 
    <set name="Assets" cascade="all" inverse="true" access="field.camelcase-underscore" lazy="true"> 
     <key column="AssetGroupID"/> 
     <one-to-many class="Asset"/> 
    </set> 
    </class> 
</hibernate-mapping> 

<?xml version="1.0" encoding="utf-8" ?> 
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" 
        assembly="TestNHibernate" 
        namespace="TestNHibernate.Models" auto-import="true"> 
    <class name="Asset"> 
    <id name="Id" column="Id" type="guid"> 
     <generator class="guid"></generator> 
    </id> 
    <property name="Name" type="string" not-null="true"/> 
    <many-to-one name="AssetGroup" column="AssetGroupID" cascade="all" lazy="false"/> 

    </class> 
</hibernate-mapping> 

folloow 같은 코드의 예를 다음입니다 :

[TestMethod] 
public void Can_Use_ISession() 
{ 
    ISession session = TestConfig.SessionFactory.GetCurrentSession(); 
    var ag = new AssetGroup { Name = "NHSession" }; 
    session.Save(ag); 

    var a1 = new Asset { Name = "s1" }; 
    var a2 = new Asset { Name = "s2" }; 

    a1.SetAssetGroup(ag); 
    a2.SetAssetGroup(ag); 

    session.Flush(); 

    Assert.IsTrue(a1.Id != default(Guid)); // ok 
    Assert.IsTrue(a2.Id != default(Guid)); // ok 

    var enumerator = ag.Assets.GetEnumerator(); 
    enumerator.MoveNext(); 
    Assert.IsTrue(ag.Assets.Contains(enumerator.Current)); // failed 

    Assert.IsTrue(ag.Assets.Contains(a1)); // failed 
    Assert.IsTrue(ag.Assets.Contains(a2)); // failed 

    var agRepo2 = new NHibernateRepository<AssetGroup>(TestConfig.SessionFactory, new QueryFactory(TestConfig.Locator)); 
    Assert.IsTrue(agRepo2.Contains(ag)); // ok 
    var ag2 = agRepo2.FirstOrDefault(x => x.Id == ag.Id); 
    Assert.IsTrue(ag2.Assets.FirstOrDefault(x => x.Id == a1.Id) != null); // ok 
    Assert.IsTrue(ag2.Assets.FirstOrDefault(x => x.Id == a2.Id) != null); // ok 

    var aa1 = session.Get<Asset>(a1.Id); 
    var aa2 = session.Get<Asset>(a2.Id); 
    Assert.IsTrue(ag2.Assets.Contains(aa1)); // failed 
    Assert.IsTrue(ag2.Assets.Contains(aa2)); // failed 

} 

내 엔터티의 기본 클래스가 여기에 있습니다 : :

public abstract class Entity<Tid> : IEquatable<Entity<Tid>> 
{ 
    [HiddenInput(DisplayValue = false)] 
    public virtual Tid Id { get; protected set; } 

    public override bool Equals(object obj) 
    { 
     if (obj == null) 
      return base.Equals(obj); 
     return Equals(obj as Entity<Tid>); 
    } 

    public static bool IsTransient(Entity<Tid> obj) 
    { 
     return obj != null && Equals(obj.Id, default(Tid)); 
    } 

    private Type GetUnproxiedType() 
    { 
     return GetType(); 
    } 

    public virtual bool Equals(Entity<Tid> other) 
    { 
     if (ReferenceEquals(this, other)) 
      return true; 
     if (!IsTransient(this) && !IsTransient(other) && Equals(Id, other.Id)) 
     { 
      var otherType = other.GetUnproxiedType(); 
      var thisType = GetUnproxiedType(); 
      return thisType.IsAssignableFrom(otherType) || otherType.IsAssignableFrom(thisType); 
     } 
     return false; 
    } 

    public override int GetHashCode() 
    { 
     if (Equals(Id, default(Tid))) 
     { 
      return base.GetHashCode(); 
     } 
     else 
     { 
      return Id.GetHashCode(); 
     } 
    } 

} 

public class AssetGroup : Entity<Guid> 
{ 
    public AssetGroup() 
    { 
     this._assets = new HashedSet<Asset>(); 
    } 


    virtual public string Name { get; set; } 

    private ISet<Asset> _assets; 
    virtual public ISet<Asset> Assets 
    { 
     get { return _assets; } 
     protected set { _assets = value; } 
    } 

    virtual public bool AddAsset(Asset asset) 
    { 
     if (asset != null && _assets.Add(asset)) 
     { 
      asset.SetAssetGroup(this); 
      return true; 
     } 
     return false; 
    } 

    virtual public bool RemoveAsset(Asset asset) 
    { 
     if (asset != null && _assets.Remove(asset)) 
     { 
      asset.SetAssetGroup(null); 
      return true; 
     } 
     return false; 
    } 
} 

public class AssetGroup : Entity<Guid> 
{ 
    public AssetGroup() 
    { 
     this._assets = new HashedSet<Asset>(); 
    } 

    virtual public string Name { get; set; } 

    private ISet<Asset> _assets; 
    virtual public ISet<Asset> Assets 
    { 
     get { return _assets; } 
     protected set { _assets = value; } 
    } 

    virtual public bool AddAsset(Asset asset) 
    { 
     if (asset != null && _assets.Add(asset)) 
     { 
      asset.SetAssetGroup(this); 
      return true; 
     } 
     return false; 
    } 

    virtual public bool RemoveAsset(Asset asset) 
    { 
     if (asset != null && _assets.Remove(asset)) 
     { 
      asset.SetAssetGroup(null); 
      return true; 
     } 
     return false; 
    } 
} 

내 TestCode은 다음과 같다

1ah 코드에서 어떤 부분이 실패했는지 설명했습니다. 도와주세요. 계단식으로 저장된 엔티티는 ICollection Contains/Remove와 호환되지 않습니다. 애셋 a1 a2는 저장되고 부모 컬렉션에 있습니다. 예를 들어 Linq FirstOrDefault에서 찾을 수 있습니다. 그러나 Collection의 Contains and Remove는 그들을 찾지 못합니다. Contains() 또는 Remove()가 호출되는 동안 Collection에서 GetHashCode를 사용합니다.

+0

Iesi HashSet을 사용하고 있습니다. –

답변

2
Assert.IsTrue(ag.Assets.Contains(a1)); // failed 

이것은 실제로 실패 할 수 있습니다. 양방향 관계를 관리해야합니다.

virtual public bool AddAsset(Asset asset) 
{ 
    if (asset != null && _assets.Add(asset)) 
    { 
     asset.AssetGroup = this; 
     return true; 
    } 
    return false; 
} 

... and a corresponding remove 

자산에서 :

virtual public bool SetAssetGroup(AssetGroup group) 
{ 
    this.AssetGroup = group; 
    group.Assets.Add(this); 
} 

참고 코드 및 위와의 차이 이것에 의하여 나는 AssetGroup에서

을 의미한다. 이것이 유일한 방법은 아니지만 맵핑에 가장 독립적이며 안전한 방법입니다. 매핑에 inverse = true를 설정했는지 여부에 관계없이 작동합니다. 나는 그것을 너무 많이 생각하지 않고도 기본적으로 그렇게한다.

모델 번호 외부에서으로 작업 할 때는 AddXXX, RemoveXXX, SetXXX를 사용합니다. 내부에서 모델로 작업 할 때 속성과 컬렉션을 직접 참조하십시오. 컨벤션으로 채택하면 일반적인 양방향 매핑 시나리오의 대부분에서 괜찮습니다.

Assert.IsTrue(ag2.Assets.Contains(aa1)); 

AG2 새로운 쿼리입니다이 확인을해야하므로 세션이 내가 돈 객체를 캐시하지 않는 ... :

이 코드가 실패 이유를 잘 모르겠어요,이 말을 데 그것이 생각하지 않는다. .. 그러나 나는 확실하지 않다.

+0

답장을 보내 주셔서 감사합니다. 내 코드를 업데이트했습니다. 어떻게 든 문제는 session.Refresh (ag)에 의해 해결됩니다. –

0

session.Refresh (ag)를 추가 한 다음 작동합니다. 세션 새로 고침은 값 비싼 작업입니까? 어떤 대안이 있습니까

[TestMethod] 
public void Can_Use_ISession() 
{ 
    ISession session = TestConfig.SessionFactory.GetCurrentSession(); 
    var ag = new AssetGroup { Name = "NHSession" }; 
    session.Save(ag); 

    var a1 = new Asset { Name = "s1" }; 
    var a2 = new Asset { Name = "s2" }; 

    a1.SetAssetGroup(ag); 
    a2.SetAssetGroup(ag); 

    session.Flush(); 
    session.Refresh(ag); 

    Assert.IsTrue(a1.Id != default(Guid)); // ok 
    Assert.IsTrue(a2.Id != default(Guid)); // ok 

    var enumerator = ag.Assets.GetEnumerator(); 
    enumerator.MoveNext(); 
    Assert.IsTrue(ag.Assets.Contains(enumerator.Current)); // failed 

    Assert.IsTrue(ag.Assets.Contains(a1)); // failed 
    Assert.IsTrue(ag.Assets.Contains(a2)); // failed 

    var agRepo2 = new NHibernateRepository<AssetGroup>(TestConfig.SessionFactory, new QueryFactory(TestConfig.Locator)); 
    Assert.IsTrue(agRepo2.Contains(ag)); // ok 
    var ag2 = agRepo2.FirstOrDefault(x => x.Id == ag.Id); 
    Assert.IsTrue(ag2.Assets.FirstOrDefault(x => x.Id == a1.Id) != null); // ok 
    Assert.IsTrue(ag2.Assets.FirstOrDefault(x => x.Id == a2.Id) != null); // ok 

    var aa1 = session.Get<Asset>(a1.Id); 
    var aa2 = session.Get<Asset>(a2.Id); 
    Assert.IsTrue(ag2.Assets.Contains(aa1)); // failed 
    Assert.IsTrue(ag2.Assets.Contains(aa2)); // failed 

} 
+0

새로 고침은 데이터베이스로 이동하여 개체를 다시로드합니다. 여기에 귀하의 경우에는 무의미합니다. a1.SetAssetGroup (ag)을 호출 한 후에는 코드에서 메소드가 ag.Assets에 a1을 추가해야합니다. –

+0

예, Asset.SetAssetGroup (ag)은 자산 그룹 컬렉션에 자산을 추가합니다.알 수없는 이유로 nseverate Isei HashSet의 Contains가 내 엔터티 의 Equals() 재정의를 사용하지 않습니다. 그리고 session.Refresh (ag)를하지 않으면 Contains() 호출이 false를 반환합니다. 비슷하게 HashSet Remove (..)도 실패하고 false를 반환합니다. –

관련 문제