2011-08-15 17 views
0

私は、AssetGroupエンティティに、Assetエンティティとの1対多の関係を持っています。 EqualsとGetHashCodeをオーバーライドするEntity基本クラスがあります。私はch 20 parent childNHibernateの1対多の関係の問題カスケードを使ったコレクション

<?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は以下の通りです

I haどの部品がコードで失敗したかをコメントしました。助けてください。カスケードによって保存されるエンティティは、ICollectionのContains/Removeと互換性がありません。資産a1 a2は保存され、親のコレクション内にあります。たとえば、私はLinq FirstOrDefaultによってそれらを見つけることができます。しかし、CollectionのContainsと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 

::このことで私は、AssetGroupで

を意味

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

あなたのコードと上記1との違いに注意してください。 これはこれを行う唯一の方法ではありませんが、それはマッピングに不可欠で安全な方法です。マッピングにinverse = trueを設定するかどうかにかかわらず、うまくいきます。私はそれをあまり考えすぎることなく、デフォルトでそれを行います。

モデルを外部から操作する場合は、AddXXX、RemoveXXX、SetXXXを使用します。内部ののモデルを使用して作業する場合は、そのプロパティとコレクションを直接参照してください。これをコンベンションとして採用すれば、一般的な双方向マッピングシナリオのほとんどでOKになります。

このコードが失敗した理由

はこのことを言って、私はよく分からない:セッションオブジェクト、Iドンをキャッシュされない限り、それは... [OK]をする必要がありますので

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

Refreshはデータベースに行き、オブジェクトをリロードします。あなたのケースでは無意味です。 a1.SetAssetGroup(ag)を呼び出した後、コードでメソッドがag.Assetsにもa1を追加するようにする必要があります。 –

+0

はい、Asset.SetAssetGroup(ag)はアセットをアセットグループコレクションに追加します。何も知られていない理由のために、nseverateのIsei HashSetのContainsは、私のエンティティのEquals()オーバーライドを使用しません。私がsession.Refresh(ag)しないと、Contains()呼び出しはfalseを返します。同様に、HashSetのRemove(..)も失敗し、falseを返します。 –

関連する問題