如何处理ISet中的NHibernate父子关系和生成的ID?

如何处理ISet中的NHibernate父子关系和生成的ID?,nhibernate,equals,Nhibernate,Equals,不到一年前,我就进入了NHibernate的世界,现在我仍在发展自己的能力 这方面的“个人”最佳实践和架构解决方案。。。现在我面临着一个挑战 这是一个非常简单的问题,我想让有经验的人提出意见或建议 更多的专业知识 场景如下:直接的父/子关系存储在父端的ISet中 课程: public class IDPersisted<IdentifierType> { private IdentifierType _id; public IDPersisted() {

不到一年前,我就进入了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; }

    ...
}
创建新字段实例时,其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 ();           
    }