Reputation: 2061
So in a typical model where you have a parent that can have many children and a child that can have only one parent, how do you manage the adding of children. I have been using this approach;
public class Parent
{
public Parent()
{
Children = new List<Child>();
}
public IList<Child> Children
{
get;
private set;
}
}
public class Child
{
public Parent Parent
{
get;
set;
}
}
var child = new Child();
var parent = new Parent();
parent.Children.Add(child);
child.Parent = parent;
Problem is that everywhere i want to add a new child i've got to remember to add a reference to both the child and parent and its a bit of a pain. I could just add an AddChild method to the parent class and make that responsible for adding children - the problem now is that there is 2 ways to add a child, through the Children property and the method. So is this a better solution?
public class Parent
{
public Parent()
{
children = new List<Child>();
}
private IList<Child> children
{
get;
private set;
}
public IEnumerable<Child> Children
{
get
{
return children;
}
}
public void AddChild(Child child)
{
children.Add(child);
child.Parent = this;
}
}
Are there any guidences for best practices on this, and what do you do?
Upvotes: 11
Views: 3952
Reputation: 777
If you have a foreign key in your DB, and you are using Identity(SQL Server) to generate your primary keys, you are going to NEED the backlink from the child to the parent. Else, the insert will complain on child because nhibernate needs to do some back and forth for the parent ID, but it hasn't done it yet.
What we ended up doing to get rid of backlinks : Use the NHibernate HiLo generator. This way, NHibernate always has the Ids it needs to insert your parent/child relations.
<3 !
Upvotes: 0
Reputation: 3783
I support the accepted solution to this question, however the solution presented is not complete, as using private fields requires some extra configuration in the mappers. For the benefit of others, the following is the complete solution:
public partial class Test
{
private readonly IList<Child> children = new List<Child>();
public virtual IEnumerable<Child> Children
{
get
{
return children;
}
}
}
Note that the publicly exposed collection must be virtual for NHibernate to use it. I also like to make it a readonly field that is initialised when the class is created to ensure it exists in all scenarios. Here is the associated mapper:
public class TestMap : ClassMap<Test>
{
...
HasMany(s => s.Children).Access.CamelCaseField();
}
The Access property tells NHibernate to use the private field when mapping values into the model. There are also other options on the Access property allowing various naming configurations to be used.
Upvotes: 0
Reputation: 12698
I'm using public IEnumerable with Add|Remove methods approach.
However I don't like it much, because it's not intuitive, and polutes class definition.
I'm wondering why people don't use CustomCollection where they override Add, Remove, Replace functions?? (like it's done everywhere in MS code) ????
Upvotes: 1
Reputation: 64658
This is not an NHibernate problem at all.
You should implement the AddChild method. The classes are responsible for their consistency, so they shouldn't expose anything that should not be available. For instance, the (mutable) Children list should be hidden. Exposing an IEnumerable is a good idea.
Your second code is a good starting point. You probably need some more methods, like RemoveChild or CoundChildren.
Upvotes: 10
Reputation: 17655
I do it like that except that I don't use property for private list
private IList<Child> _children
public IEnumerable<Child> Children
{
get
{
return children;
}
}
Upvotes: 2
Reputation: 17562
I don't like all the extra AddXXX()
and RemoveXXX()
methods cluttering my entity interfaces. Instead, I have a custom list that raises events when the Add()
and Remove()
methods are called.
The linking then happens in the event handlers:
public class Course()
{
public Course()
{
this.Topics = new EntityList<Topic>();
this.Topics.AddItem += new AddItemEventHandler<Topic>(Topic_AddItem);
this.Topics.RemoveItem += new RemoveItemEventHandler<Topic>(Topic_RemoveItem);
}
public EntityList<Topic> Topics { get; private set; }
private void Topic_AddItem(Topic item, object args)
{
// Replace with your linking code:
EntityLinker.Link(this).With(item, args);
}
private void Topic_RemoveItem(Topic item, object args)
{
// Replace with your unlinking code:
EntityLinker.Unlink(this).From(item, args);
}
}
Upvotes: -1
Reputation: 56984
I do it like this:
public class Parent
{
private ISet<Child> _children = new HashedSet<Child>();
public ReadOnlyCollection<Child> Children
{
get{ return new List(_children).AsReadOnly(); }
}
public void AddChild( Child c )
{
if( c != null && !_children.Contains (d) )
{
c.Parent = this;
_children.Add (c);
}
}
}
So, in fact, that's a bit what Stefan says as well. I just expose a readonly-copy of the Children list, so that you can easily iterate over the children of a parent, and get the number of children that the parent has. Adding and removing children to the parent, has to be done using the AddChild & RemoveChild member methods.
Upvotes: 5