如何处理ISet中的NHibernate父子关系和生成的ID?
不到一年前,我就进入了NHibernate的世界,现在我仍在发展自己的能力 这方面的“个人”最佳实践和架构解决方案。。。现在我面临着一个挑战 这是一个非常简单的问题,我想让有经验的人提出意见或建议 更多的专业知识 场景如下:直接的父/子关系存储在父端的ISet中 课程:如何处理ISet中的NHibernate父子关系和生成的ID?,nhibernate,equals,Nhibernate,Equals,不到一年前,我就进入了NHibernate的世界,现在我仍在发展自己的能力 这方面的“个人”最佳实践和架构解决方案。。。现在我面临着一个挑战 这是一个非常简单的问题,我想让有经验的人提出意见或建议 更多的专业知识 场景如下:直接的父/子关系存储在父端的ISet中 课程: public class IDPersisted<IdentifierType> { private IdentifierType _id; public IDPersisted() {
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; }
...
}
创建新字段实例时,其ID设置为Guid.Empty
(全为零),然后调用AddField,并将该字段添加到具有该ID的集合中……然后,仅在SportCentersDAO.Save
调用时,才将实际Guid分配给Field.ID
这打破了关于NHibernate的每个规范/文档/书籍中的一条规则,即:
当实例存储在ISet中时,千万不要更改它的ID(因为正如我写的那样,ID是Equals()比较实例的属性)
在将字段添加到集合之前为其提供ID的Guid值将使我进入邪恶的“分配”ID,如果您要回答不依赖ID来获得Equals/GetHashCode
,并找到与自然模型相关的键……那么,我相信我从未在这些属性中找到过提供不可变性/唯一性的“键”值得
我做错什么了吗!?你对此有什么看法
提前谢谢你,Peter。我的实体的基类与你的基本相同 基类已重写Equals/GetHashcode方法等。。。 在基类中的Equals方法的实现中,我检查实体是否是暂时的(还不是持久的)。当分配给实体的Id仍然是默认值时,实体是瞬态的 在这种情况下,我不会基于Id检查相等性。
如果必须比较的两个实体都是暂时的,我使用ReferenceEquals方法来确定相等性 在我的
GetHashCode
实现中,我执行以下操作:
- 我的实体中有一个私有成员变量
,它是可为空的类型oldHashcode
- 当oldHashCode不为null时,我返回
oldHashCode
- 否则,当实体是瞬态的时,我调用
并将返回的值存储在base.GetHashCode()
中。我返回这个值oldHashCode
- 否则,我将根据Id生成Hashcode。
Id.GetHashCode()
使用这种“策略”,到目前为止,我从未遇到过任何奇怪的问题…我喜欢检查Equals实现中的默认值以检测尚未持久化的实体。但我不了解您在GetHashCode中所做的事情,老实说,这更像是一种“正确”的直接实现。你能帮我发些密码吗?!非常感谢您的回答。我不认为这是一种黑客行为,因为它符合一条规则,即哈希代码在对象的生存期内不应更改。
<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>
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);
}
private int? _oldHashCode;
public override int GetHashCode()
{
if( _oldHashCode.HasValue )
{
return _oldHashCode.Value;
}
if( IsTransient )
{
_oldHashCode = base.GetHashCode ();
return _oldHashCode.Value;
}
return Id.GetHashCode ();
}