English 中文(简体)
如何处理具有ISet中和生成的ID中子级的NHibernate父子关系?
原标题:How to deal with NHibernate parentchild relationship with childs in ISet and generated IDs?

Having entered the world of NHibernate less than one year ago, I m still developing my "personal" best practice and architectural solutions in this area... now I m facing a pretty simple issue on which I d like to have opinions or suggestions by somebody with more expertise.

情景如下:直接父子关系存储在父节点侧的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字段,在类实例生命周期中被认为是不可变的。SportCenter(父级)和Filed(子级)类都使用Guid标识符进行持久化。SportCenter保存一个集合(实际上是一个Set)的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>

我已采纳你为测绘这种关系而获得的所有基本建议:反向=“真实”,《一套规则》不能直接获得(进入“现场”)、提单和不发薪。

在我的BLL中,我有一个方法来在特定的运动中心内创建一个新的领域:

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被设置为(即全零),然后我调用AddField,该字段将使用该ID添加到Set中...然后仅在调用时,真正的Guid被分配给field.ID。

This breaks one of the rules you can find in every spec/doc/book about NHibernate that is: never change the ID of an instance while it is stored in an ISet (since the ID is the property by which instances are compared by Equals() as I wrote uphere).

在将字段添加到Set之前,为其提供一个GUID值的ID将带我进入邪恶的“分配”ID,并且如果您回答说不要依赖于ID进行Equals/GetHashCode并找到自然的与模型相关的关键字...好吧,我不相信我曾经在这些属性中找到过提供不可变性/唯一性所应具备的“键”。

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

谢谢你事先的帮助,彼得。

问题回答

我有与你差不多种类的基类用于我的实体。

The base class has overriden Equals / GetHashcode methods, etc... In my implementation of the Equals method in the base class, I check whether the entity is transient (not yet persistent). An entity is transient, when the Id that has been assigned to it, is still the default value.

When this is the case, I do not check the equality based on the Id.
If both entities that have to be compared, are transient, I use the ReferenceEquals method in order to determine equality.

在我的GetHashCode的实现中,我这样做:

  • I have a private member variable oldHashcode in my entity, which is of a nullable type.
  • when oldHashCode is not null, I return oldHashCode.
  • else, when the entity is transient, I call base.GetHashCode() and store the returned value in oldHashCode. I return this value.
  • 否则,我以Id. 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 ();           
        }
    

使用这种策略,到目前为止我从未遇到任何奇怪的问题...





相关问题
nHibernate one-to-many inserts but doesnt update

Instead of getting into code, I have a simple question. Default behavior for a simple one-to-many is that it inserts the child record then updates the foreign key column with the parent key. Has ...

How Do I copy an existing nhibernate object as a new object?

I a persisted NHibernate object that I would like to repersist as a new entity. How do I get NHibernate to save this object as if it was a new? I am thinking I might create a session interceptor to ...

join across databases with nhibernate

I am trying to join two tables that reside in two different databases. Every time, I try to join I get the following error: An association from the table xxx refers to an unmapped class. If the ...

WPF - MVVM - NHibernate Validation

Im facing a bit of an issue when trying to validate a decimal property on domain object which is bound to a textbox on the view through the viewmodel. I am using NHibernate to decorate my property on ...

NHibernate Search in a List using ICriteria

I have my class X : public class ClassX { public virtual IList<ClassY> ListY { get; set; } ... } My ClassX mapping (using Fluent) ... HasMany<ClassX>(x => x.ListY ) ....