我的AssetGroup实体与Asset实体具有一对多的关系。有一个实体基类,它覆盖了Equals和GetHashCode。我正在关注ch 20 parent child
的例子<?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>
代码如下:
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已保存,并且它们位于父级的Collection中。例如,我可以通过Linq FirstOrDefault找到它们。但是Collection的Contains and Remove将无法找到它们。我注意到Collection使用GetHashCode,同时调用Contains()或Remove()。
答案 0 :(得分:3)
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来自一个新的查询,所以应该没问题...除非会话缓存了对象,我认为它没有...但我不确定。
答案 1 :(得分: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
}