views:

819

answers:

4

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?

+5  A: 

This is not 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.

Stefan Steinegger
Talking the second example my only concern is how this may work with Linq to NHibernate. As the actual mapped property is now private i would assume that it would not be visable. Any thoughts?
Gareth
You can specify in your mapping that NHibernte should use the field instead of the property when setting a value. (Take a look at the access attribute for instance).
Frederik Gheysels
Yes, what I was getting at was that when Linq to NHibernate is finished how could you use it if the field is private and the public access only returns a reference to the original collection. Eg.session.Linq<Parent>().Where(x => x.Children.Name.Equals("Jonnie"))
Gareth
I found the answer to this in another thread (http://stackoverflow.com/questions/195528/bidi-associations-and-nhibernate-mapping) one day by accident and thought i'd update here as well. Basically to get around this use a naming strategy - in this case the mapping would be <many-to-one name="Children" access="field.camelcase" />field.camelcase
Gareth
+2  A: 

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.

Frederik Gheysels
A: 

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);
   }   
}
Vijay Patel
I don't like this approach. :) There is to much 'magic' going on behind the scenes.I remember that Ayende once used this approach as well (in NHibernate generics if i'm not mistaken), but now I do not see him using this approach any more.I just find that the 'link' logic is to much hidden.Next to that, I don't see adding Addxxxx methods to my entities as cluttering of the interface. Moreover, I find it better since it is more explicit.
Frederik Gheysels
This does not look very readable to me.
UpTheCreek
A: 

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;
    }
}
epitka