9

Using Entity Framework Code First, I have something like:

public class Foo
{
    public int Id { get; set; }

    public List<Bar> Bars { get; set; }
}    

Foo foo = (from f in ctx.Foos.Include("Bars") where f.Id == 42 select f).Single();

// At this point foo.Bars is populated

ctx.Entry(foo).State = EntityState.Detached;

// At this point foo.Bars is an empty List

Why does detaching an object cause it's property public List<string> Bars, which was explicitly and successfully included, to be emptied?

What is the correct procedure to detach an object that may have many properties?

Eric J.
  • 147,927
  • 63
  • 340
  • 553

1 Answers1

14

The reason why the list is emptied is a combination of two rules in Entity Framework:

  1. When you detach an object only this object itself is detached without the objects any navigation properties refer to.

  2. The ObjectContext/DbContext does not allow to hold an object graph which is partially attached to the context and partially detached. Although this can happen as a temporary state when using POCOs EF will always fix this temporary state by automatically attaching detached objects in the graph inside of various methods (like Add, Attach, setting the state of an entity, etc.) or latest when SaveChanges is called.

This means that when you detach the root object from the context, EF will clear the list of children because: a) the children stay attached (rule 1) and b) a mix of detached and attached objects in the graph is not allowed (rule 2).

As far as I can tell there is no way to detach an object graph from the context while maintaining the original tree structure. You can detach the parent and then the children one after each other. As a result you have detached all objects of the tree from the context but the tree is destroyed at the same time - each navigation property is nullified.

The main purpose of detaching entities manually is to release them for garbage collection in situations where you have memory resource limitations and don't want and need to hold a huge amount of objects in the context. For this purpose it doesn't matter that the graph structure is destroyed.

I don't know why you need to detach objects from the context. But keep in mind that there is also the option to load entities from the database without attaching them to the context in the first place, like using AsNoTracking().

Another answer about the problem with some references to MSDN documentation is here: https://stackoverflow.com/a/7693732/270591

Community
  • 1
  • 1
Slauma
  • 175,098
  • 59
  • 401
  • 420
  • The DbContext goes out of scope long before my object is ever disposed. Will loading my object with `AsNoTracking()` allow the DbContext to be collected, or will my object still maintain a reference to it? That's my main concern. – Eric J. Apr 27 '12 at 01:19
  • @EricJ.: You are using EF code first, hence your entities are POCOs. If you don't use lazy loading (and it looks you don't because `Foo.Bars` is not `virtual`) then the entities are not proxies and they have no reference to the context. So, answer is yes, the context will be garbage collected even if you still hold a reference to your entity. – Slauma Apr 27 '12 at 10:54
  • If the entities are proxies, then the context will not be garbage collected until the entities are disposed? – Eric J. Apr 27 '12 at 19:02
  • @EricJ.: I think so, yes. Because a proxy has internally a reference to context to perform lazy loading. But I am not sure, maybe ask a new question about this. – Slauma Apr 27 '12 at 19:26
  • Great explanation. But what if you wanted to integrate cache (say Redis) with EF Core? It can create a whole lot of problems with tracking especially with navigation properties. Any insight on that? – Zion Hai Jan 14 '21 at 11:56