2011-08-15 56 views
0

我有AssetGroup實體與資產實體具有一對多關係。有一個實體基類覆蓋了Equals和GetHashCode。我下面的ch 20 parent child與NHibernate一對多關係問題級聯收集

<?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的例子:

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情況如下:

[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(); 
     } 
    } 

} 

我哈哈已經評論哪些部分在代碼中失敗了。請幫忙。看來通過級聯保存的實體與ICollection Contains/Remove不兼容。資產a1 a2已保存,它們位於父級集合中。例如,我可以通過Linq FirstOrDefault找到它們。但集合的包含和刪除將無法找到它們。我注意到Collection使用GetHashCode而Contains()或Remove()被調用。

+0

我正在使用Iesi HashSet –

回答

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

這可能確實會失敗。您必須管理雙向關係。我的意思是,

在AssetGroup:

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); 
} 

注意你的代碼和上面的區別之一。 這不是唯一的方法,但它是最不依賴映射的安全方法......因此,無論您是否在映射上設置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)之後,您的代碼應該確保該方法還將a1添加到ag.Assets。 –

+0

是,Asset.SetAssetGroup(ag)確實將資產添加到資產組集合。由於某些未知原因,nhibernate Isei HashSet的Contains不使用我的實體的Equals()重寫。如果我不session.Refresh(ag),Contains()調用返回false。類似的HashSet刪除(..)也會失敗並返回false。 –

相關問題