Behavior of Saving Objects with Children

Sep 19, 2011 at 10:47 PM

I have a Parent class that has a list of Child classes. The Child class also has a reference back to it's Parent.

public class Parent
{
    public Guid Id { get; set; }
    public List<Child> Children { get; set; }
}

public class Child
{
    public Guid Id { get; set; }
    public Parent Parent { get; set; }
}

I was trying to save a new Child with a reference to it's parent. It saves fine, and I can retrieve it fine, and it has it's parent attached to it. If I retrieve the Parent, it has a list of 0 Child objects on it. Is this the intended behavior or am I doing something wrong? Should I only be adding the Child to the Parent and saving the Parent instead? They are both setup as database objects.

Coordinator
Sep 20, 2011 at 12:07 AM

An issue can arise if you are using triggers to generate the id. The master (parent) saves the reference to the child before the child is saved, so the trigger is not invoked on the child. It is saved as an empty guid and then never references. To work around this, save the child first, then save the parent. You only have to do this on inserts.

Sep 20, 2011 at 2:26 AM

I'm not currently using triggers for the Id generation, but I may switch to that.

So, is it a requirement that child must be added to the parent, and the parent is saved for the child to show up in the list of children of the parent? If I do it that way, I can still load the child by id. If I just save the child with the parent attached to it, it doesn't seem to show up in the list of the parent children.

Is this the recommended way of saving parent/child relationship?

 

var parent = new Parent();
var child = new Child();

// This is the only way I can get the child to show up in the parent's list of children
parent.Children.Add( child );
Database.Save( parent );

// This is so the Trigger on the child is ran
Database.Save( child );

 

 

This is the way I have been doing it that didn't work:

 

var parent = new Parent();
var child = new Child();

// This way seems to not understand the relationship 
// and the child isn't added to the parent list
// of children
child.Parent = parent;
Database.Save( child );

 

 

Coordinator
Sep 20, 2011 at 2:35 AM

The code above behaves exactly as I'd expect. Sterling isn't responsible for managing your relationships, only saving the ones you expose.

So in the first examle, you are linking the child to the parent, but you aren't setting the parent on the child. Sterling won't do this because it's not designed to alter your classes or make presumptions. Just because the child is in the parent list doesn't imply that the child should also be modified to update the parent.

The same with the second example. It is behaving exactly as I would expect. You set the parent on the child. That's great - when you reload the child, you should also get a fully reloaded reference to the parent. It isn't about Sterling not understanding relationships - you're not setting them. Forget Sterling for a minute. If you write a unit test and just set the parent of the child, the child is NOT added to the parent. You can check that in code, no Sterling. You'll have a parent with a set of empty children, and a child with parent that points to the parent. It's your responsibility to update the references correctly, i.e. if you set the parent of the child, also add the child to the parent.

Most implementations I've seen do this provide a method on the parent like AddChild that takes care of adding the child to the children collection and then setting the parent on the child.

Again, not a Sterling issue - Sterling saves them exactly as is. I think if you debug, you'll find in your first example the child has no reference to the parent, and in the second the parent has no reference to the child because you've not set that up.

Sep 20, 2011 at 2:51 AM

OK, that all makes sense. I actually forgot to add the "child.Parent = parent;" part in the first example. The "AddChild" method is something I'm used to doing in NHibernate.

What is confusing me with the whole thing is, what happens when the parent has the child in it's items and is saved, and the child has the parent and is saved? Is there only a single parent and a single child saved? Meaning Sterling figures out that there is a Parent and Child table definition and only saves a single instance of each. Or is there a parent with it's children saved, and a child with it's parent saved. So 4 "entities saved". 

I just ran a little test and see that if I just add the child the parent's list of children, I'm still able to load the child by it's Id. I don't have to explicitly save it, but I still should for trigger purposes.