问题
I have 2 class'es that are handled by NHibernate : AssetGroup , Asset The AssetGroup has a ISet _assets collection. The constructor of AssetGroup will say
_assets = new HashSet<Asset>();
I have some operation to add , remove asset in AssetGroup
public abstract class Entity<Tid>
{
public virtual Tid Id { get; protected set; }
public override bool Equals(object 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 (other == null)
return false;
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<int>
{
public AssetGroup()
{
this._assets = new HashedSet<Asset>();
}
virtual public Guid SecurityKey {get; set;}
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))
{
return true;
}
return false;
}
virtual public bool RemoveAsset(Asset asset)
{
Asset target = null;
foreach (var a in _assets)
{
var x = a.GetHashCode();
var b = a.Equals(asset);
if (a.Equals(asset))
target = a;
}
if (target == null)
return false;
if (asset != null && _assets.Remove(target))
{
return true;
}
return false;
}
}
////////////////////////////////////////
public class Asset : Entity<int>
{
public Asset()
{
SecurityKey = Guid.NewGuid();
}
public virtual Guid SecurityKey { get; set; }
virtual public int AssetGroupID { get { return (AssetGroup != null ? AssetGroup.Id : 0); } }
virtual public string Name { get; set; }
virtual public AssetGroup AssetGroup { get; set;}
virtual public void SetAssetGroup(AssetGroup assetGroup)
{
AssetGroup prevRef = AssetGroup;
if (prevRef == assetGroup)
return;
AssetGroup = assetGroup;
if (prevRef != null)
prevRef.Assets.Remove(this);
if (assetGroup != null)
assetGroup.Assets.Add(this);
}
}
The RemoveAsset fails to remove asset . I have a foreach to check if the asset exists in the _assets . I put breakpoints to trace thru it and foreach loop can find the asset (targe) to be RemoveAsset'ed . Strangely enough , when I ask _assets to Remove the target. It fails to remove and return false. Also if I asks _assets.Contains(target) .. it also return false .. even if the foreach loop in the RemoveAsset can find the target...
the two nhibernate mappings are
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
assembly="MySystem.Domain"
namespace="MySystem.Domain" auto-import="true">
Can someone help me ?
回答1:
are you sure this is the actual code executing? It seems to me that even if you override Equals and GetHashCode in some malfunctioning way, as soon as you find an Asset and target is assigned with an object from _assets, the Remove method should never fail when called with target as it definitly is contained in the Set. I did a short test and the set behaved as expected.
回答2:
I think I just faced the same issue, and the "problem" was that I added the item to the HashedSet and afterwards changed it (to assign IDs), changing the result of the GetHashCode.
I think this means that the object ended up in a different bucket in the inner dictionary, hence Contains returning false
.
回答3:
Yep I have an almost identical implementation of your base Entity object (we must have read the same book :D).
As Bruno suggests the problem lies with the fact that GetHashCode returns different values (either the base objects object reference, or a primary key value). in most situations this is not a problem for me since I rarely change an object once it has been saved but under some circumstances, as per below, this can cause problems:
FuelTank newtank = new FuelTank();
// Below stores tank in the Tanks Set using the
//objects reference identity (since it is a new object)
vessel.Tanks.Add(newtank);
//returns true because newtank.Id has not changed (remove
//uses Contains internally to see if an object can be removed)
vessel.Tanks.Contains(newtank);
//now newtank.Id changes because a primary key is
//generated for it at this point
Repository.Save(vessel);
// returns false because newtank is still stored under
//its object reference in the set but its id is now the id of a PK
vessel.Tanks.Contains(newtank);
来源:https://stackoverflow.com/questions/5040043/nhibernate-iesi-iset-fails-to-remove