不到一年前进入NHibernate的世界,我仍在开发我的 这方面的“个人”最佳实践和建筑解决方案......现在我面临着一个问题 非常简单的问题,我希望有人提出意见或建议 更多的专业知识。
场景如下:存在于父节点的ISet中的直接父/子关系。
类:
public class IDPersisted<IdentifierType>
{
private IdentifierType _id;
public IDPersisted()
{
_id = default(IdentifierType);
}
public IDPersisted(IdentifierType id)
{
_id = id;
}
}
public class SportCenter : IDPersisted<Guid>
{
...
private ISet<Field> _fields = new HashedSet<Field>();
public bool AddField(Field field)
{
field.SportCenter = this;
return _fields.Add(field);
}
public bool RemoveField(Field field)
{
field.SportCenter = null;
return _fields.Remove(field);
}
...
}
public class Field : IDPersisted<Guid>
{
public Field(String name)
{
Name = name;
}
public String Name { get; set; }
public SportCenter SportCenter { get; set; }
...
}
正如您所看到的,我有一个实现通用标识符的基类,Equals / GetHashCode和== /!=运算符都依赖于ID字段,该字段在类实例的生命周期中被认为是不可变的。 SportCenter(父级)和Filed(子级)类都使用Guid标识符保留。 SportCenter拥有一个集合(实际上是一组)Field实例,用于管理Add / RemoveField方法中的双向关系。
映射:
<class name="SportCenter" table="SportCenters" lazy="false">
<id name="ID" column="SportCenterID" >
<generator class="guid.comb" />
</id>
...
<set name="Fields" table="Fields" inverse="true" lazy ="true" cascade="all-delete-orphan" access="field.camelcase-underscore">
<key column="SportCenterID" />
<one-to-many class="Field" />
</set>
</class>
<class name="Field" table="Fields" lazy="false">
<id name="ID" column="FieldID" type="Guid">
<generator class="guid.comb" />
</id>
<property name="Name" column="Name" type="String" not-null="true" />
...
<many-to-one name="SportCenter" column="SportCenterID" class ="SportCenter" not-null="true" lazy="proxy" />
</class>
我已经采用了你为映射这种关系所获得的所有基本建议:inverse =“true”,Set不能直接访问(access =“field”),cascading和not-nulls ...
在我的BLL中,我有一种在特定SportCenter中创建新Field的方法:
public void CreateField(FieldDTO fieldDTO, Guid sportcenterID)
{
// Retrieve the SportCenter
SportCenter sportcenter = SportCentersDAO.GetByID(sportcenterID);
// Prepare the new Field object
Field field = new Field(fieldDTO.Name);
...
// Add the new field to the SportCenter
sportcenter.AddField(field);
// Save the SportCenter
SportCentersDAO.Save(sportcenter);
}
当我创建新的Field实例时,其ID设置为Guid.Empty
(全是零),然后我调用AddField,并将该字段添加到具有该ID的Set中...然后,只有在SportCentersDAO.Save
电话时才会将真正的Guid分配给field.ID。
这打破了你在每篇关于NHibernate的spec / doc / book中可以找到的规则之一: 永远不会在实例存储在ISet中时更改实例的ID(因为ID是在我写完的时候通过Equals()比较实例的属性。)
在将字段添加到集合之前为字段提供ID的Guid值会将我带到邪恶的“已分配”ID,如果您要回答不依赖于Equals/GetHashCode
的ID并找到自然模型相关的关键......好吧,我不相信我曾经在这些属性中找到了一个“关键”应得的不变性/唯一性。
我做错了什么!?你对此有何看法?
提前谢谢你,彼得。
答案 0 :(得分:2)
我和我的实体有相同类型的基类。
基类有重写Equals / GetHashcode方法等... 在我对基类中的Equals方法的实现中,我检查实体是否是瞬态的(还不是持久的)。当已分配给它的Id仍然是默认值时,实体是瞬态的。
在这种情况下,我不会根据Id检查相等性 如果两个必须进行比较的实体都是瞬态的,我使用ReferenceEquals方法来确定是否相等。
在GetHashCode
的实施中,我这样做:
oldHashcode
,它是一个可以为空的类型。oldHashCode
。base.GetHashCode()
并将返回的值存储在oldHashCode
中。我将此值返回。否则,我根据Id生成Hashcode。 Id.GetHashCode()
private int? _oldHashCode;
public override int GetHashCode()
{
if( _oldHashCode.HasValue )
{
return _oldHashCode.Value;
}
if( IsTransient )
{
_oldHashCode = base.GetHashCode ();
return _oldHashCode.Value;
}
return Id.GetHashCode ();
}
使用这种“策略”,我从未遇到过任何奇怪的问题,直到现在......