1

In short, why does this fail (myChildObject is not added to the database). Note that it works with ObjectContext:

using (var db = new dbEntities())
{
    var myParentObject = db.parentObjects.First(); // this definitely exists in the db

    // this **should** attach myChildObject to the context, 
    // and therefore add it to the db on .SaveChanges() - it doesn't!
    var myChildObject = new childObject(){
        parentObject = myParentObject
    };

    db.SaveChanges();
}

This MSDN Blog Post says

You can add a new entity to the context by hooking it up to another entity that is already being tracked. This could be by adding the new entity to the collection navigation property of another entity or by setting a reference navigation property of another entity to point to the new entity.

Surely the above code should work because myChildObject references myParentObject which is a tracked object. EF should be smart enough to figure out that it needs adding into the childObjects collection. It worked fine when I was using ObjectContext and now I'm finding that I need to rewrite all of my code to get it to work with dbContext.

To get it to work I have to rewrite it like this:

using (var db = new dbEntities())
{
    var myParentObject = db.parentObjects.First(); // this definitely exists in the db

    var myChildObject = new childObject();

    myParentObject.childObjects.Add(myChildObject);

    db.SaveChanges();
}
abatishchev
  • 98,240
  • 88
  • 296
  • 433
theyetiman
  • 8,514
  • 2
  • 32
  • 41

2 Answers2

1

If you were using POCO entities with ObjectContext it worked indeed. But not because EF's change tracking worked differently than with DbContext but because the POCO entities generated by the EF 4 T4 templates contained "relationship fixup" methods.

Basically the property setter for the line parentObject = myParentObject wasn't only an object assignment but the setter included a call to a method that in the end exactly did what you are doing manually now, namely: myParentObject.childObjects.Add(myChildObject). At this point the rule "You can add a new entity to the context by hooking it up to another entity that is already being tracked" applies and myChildObject gets added to the context and inserted into the database.

For the T4 templates that generate POCO entities for DbContext those fixup methods have been removed because they were causing trouble in other scenarios. Especially when lazy loading is involved your reference assignment and the automatic call of myParentObject.childObjects... in the property setter would trigger lazy loading on the collection and load all childObjects first that are already stored for myParentObject before the new child is added to the collection. If those are thousands this is a huge unnecessary overhead, performance gets disastrous and the reason for a suddenly bad performance (just because you assigned a single reference property) isn't easy to detect if you are not aware of the fixup methods that run behind the scenes.

Here and here and here and here are examples about the confusion that relationship fixup methods were causing.

You could modify the T4 templates and add relationship fixup methods again - or if you are using Code-First just write them by hand in your entity classes - to get the old behaviour back. But this might be more complex than and at least as much work as changing your existing code the way you've outlined in your last code snippet - which I would certainly prefer over having those bothersome fixup methods back.

Community
  • 1
  • 1
Slauma
  • 175,098
  • 59
  • 401
  • 420
0

@theyetiman you're doing a little interpretation mistake of the blog text.

see:

[...]
or by setting a reference navigation property of another entity to point to the new entity.

In this part the blog said you can set a reference navigation property of a tracked object with a new entity.

like this:

[tracked entity].NavigationProperty = [new entity];

But you tring to do:

[new entity].Navigation Property = [tracked entity];

This not works. If your childObject was tracked and parentObject not you would be able to add parentObject setting it in childObject property, but the opposite is not true.

Jonny Piazzi
  • 3,684
  • 4
  • 34
  • 81
  • This isn't really an answer, but I take your point - so thanks for that. You're right. It's quite subtly worded, though, which is confusing. The official documentation needs to be more explicit with this kind of stuff, especially when they remove something like the fixup methods that @Slauma mentioned in his answer. – theyetiman Jun 10 '13 at 11:08