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.
The scenario is the following: A straight parent/child relationship stored in an ISet on parent side.
Classes:
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; }
...
}
As you can see, I have a base class implementing a generic Identifier, Equals/GetHashCode and ==/!= operators all rely on the ID field which is considered to be immutable during the life of a class instance. Both SportCenter (parent) and Filed (child) classes are persisted with a Guid identifier. The SportCenter holds a collection (actually, a Set) of Field instances managing the bidirectional relationship in the Add/RemoveField methods.
Mappings:
<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>
I've adopted all the basic advices you get for mapping this kind of relationship: inverse = "true", Set is not directly accessible (access="field"), cascading and not-nulls...
In my BLL I have a method to create a new Field inside a specific SportCenter:
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);
}
What happens is that when I create the new Field instance its ID is set to Guid.Empty
(that is all zeros) then I call AddField and the field is added to the Set with that ID...then, only at the SportCentersDAO.Save
call a real Guid is assigned to 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).
Providing the field with a Guid value for ID before adding it to the Set will take me to the evil "assigned" IDs and, if you were to answer to not rely on ID for Equals/GetHashCode
and to find a natural model-related key...well, I don't believe I've ever found in these properties which provide the immutabilty/uniqueness a "key" deserves.
Am I doing something wrong!? What's your opinion on this?
Thank you in advance, Peter.