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

时间:2010-02-18 09:28:52

标签: nhibernate equals

不到一年前进入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并找到自然模型相关的关键......好吧,我不相信我曾经在这些属性中找到了一个“关键”应得的不变性/唯一性。

我做错了什么!?你对此有何看法?

提前谢谢你,彼得。

1 个答案:

答案 0 :(得分:2)

我和我的实体有相同类型的基类。

基类有重写Equals / GetHashcode方法等... 在我对基类中的Equals方法的实现中,我检查实体是否是瞬态的(还不是持久的)。当已分配给它的Id仍然是默认值时,实体是瞬态的。

在这种情况下,我不会根据Id检查相等性 如果两个必须进行比较的实体都是瞬态的,我使用ReferenceEquals方法来确定是否相等。

GetHashCode的实施中,我这样做:

  • 我的实体中有一个私有成员变量oldHashcode,它是一个可以为空的类型。
  • 当oldHashCode不为null时,我返回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 ();           
        }
    

使用这种“策略”,我从未遇到过任何奇怪的问题,直到现在......

相关问题