none
Entities and out-of-scope ObjectContext

    Question

  •  

    I've noticed something that is a bit confusing to me.

     

    If I create a context, execute a query, then kill the context, the entities are still attached to the context (that no longer exists).

     

    This happens whether I do something like

     

    dim foos

    Using context As New MyEntities

     foos=context.Foos.ToList()

    End Using

     

    or

     

    Dim context As New My Entities

    foos=context.Foos.ToList()

    context=Nothing

     

    Relationships are in tact  (I guess this is expected as I didn't detach any of the related objects) and the state is intact.

     

    But I can't attach these to anything else.

     

    I understand I haven't explicilty Detached. But what is it that I *have* done? How can they be attached to a context that doesn't exist?

     

    RELATED QUESTION:

    It would be nice to break object graphs off of a context without having to serialize them...something like an after the fact "NoTracking". This looks close. Is there a way?

     

    Thanks

     

    Julie

    Saturday, March 08, 2008 2:13 PM

Answers

  • Currently ObjectContext does not detach objects that are being tracked when the context is disposed. This was an explicit design decision to avoid a performance hit when disposing a context with the intent to stop using both the context and all of the entities. However, when the lifetime of an entity is longer than a particular context, this can be an issue as you have seen. We have an active work item to do a better job of handling these cases so that an entity graph can be reattached to a context if it's old context has been disposed. In the mean time, you can also explicitly set the change tracker to 'null' for all the entities in your graph by using the methods on the IEntityWithChangeTracker interface (each entity type that participates in change tracking implements this interface). This will allow your entities to be reattached to another context without destroying the relationships between entities which is what will happen if you explicitly call "Detach" on all entities in the ObjectStateManager.

     

    Jeff

    Saturday, March 08, 2008 10:00 PM
    Moderator

All replies

  • Hello Julie,

    I saw also this problem but I fix extending my ObjectContext like this:

    Code Snippet

    partial class MyEntities

    {

        protected override void Dispose(bool disposing)

        {

            foreach (var entry in ObjectStateManager.GetObjectStateEntries(EntityState.Added | EntityState.Deleted | EntityState.Modified | EntityState.Unchanged))

                Detach(entry.Entity);

            base.Dispose(disposing);

        }

    }

     

    but it's only ok if I always dispose myself my context when I don't need it (most of time, I use my context in a using bloc).

     

    Else, you can (but it's really very bad) use reflection to say to your entity it is detached:

    Code Snippet

    typeof(EntityObject).GetProperty("EntityChangeTracker", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic).SetValue(myEntity, null, null);

     

     

    Saturday, March 08, 2008 3:43 PM
  • Stupid of me to try NOTHING instead of Dispose.

     

    But.... Using should dispose which is why I was using it, so maybe it's a bug?

     

    Saturday, March 08, 2008 6:37 PM
  •  

    Using disposes the object context. It's why my extension fix th bug.

    I am agree with Julie, I think it's a bug to not reset EntityChangeTracker in context entities.

    Saturday, March 08, 2008 6:49 PM
  • I've actually started wondering recently if the use of the Using/Dispose pattern heavily in an application might occasionally require a new way of doing things for the sake of simplicity and consistency.

     

    The alternative is having to deal with issues like not having a Context but having an object bound to a Context, this is not specific to the EF but it is generally applicable to any scenario where you have a serviced object without a servicer.

     

    i.e. it is very tempting to write this:

     

    public IEnumerable<Customer> GetCustomersByRegion(string region)

    {

    using (Context ctx = new Context())

    {

    return ctx.Where(c => c.Region == region);

    }

    }

     

    Which won't work at all or something like this:

     

    public IEnumerable<Customer> GetCustomersByRegion(string region)

    {

    using (Context ctx = new Context())

    {

    foreach(Customer c in ctx.Customers.Where(c => c.Region == region))

    {

    yield return c;

    }

    }

    }

     

     

    Which will only work in readonly scenarios unless you starting having to deal with servicing and plumbing explicitly, i.e. Attach/Detach etc.

     

    It seems if your DataLayer (i.e. Linq to Entities) uses the dispose pattern, perhaps one option to control complexity is to make your business rule coordination classes implement IDisposeable too and hold onto an ObjectContext, and when it disposes it can dispose the ObjectContext too.

     

    using (MyBusinessLayer layer = new BusinessLayer())

    {

    foreach(Customer c in layer.GetCustomersByRegion("Seattle")
    {

    ...;

    }

    }

     

    This is just me kind of thinking out loud though.

    Alex

    Saturday, March 08, 2008 7:28 PM
    Moderator
  • Currently ObjectContext does not detach objects that are being tracked when the context is disposed. This was an explicit design decision to avoid a performance hit when disposing a context with the intent to stop using both the context and all of the entities. However, when the lifetime of an entity is longer than a particular context, this can be an issue as you have seen. We have an active work item to do a better job of handling these cases so that an entity graph can be reattached to a context if it's old context has been disposed. In the mean time, you can also explicitly set the change tracker to 'null' for all the entities in your graph by using the methods on the IEntityWithChangeTracker interface (each entity type that participates in change tracking implements this interface). This will allow your entities to be reattached to another context without destroying the relationships between entities which is what will happen if you explicitly call "Detach" on all entities in the ObjectStateManager.

     

    Jeff

    Saturday, March 08, 2008 10:00 PM
    Moderator
  •  

    This makes sense. Knowing that you explicitly made it that way and why is helpful.And thanks for the tips.

     

    So this also answers my other question - how to unhook from the change tracker but retain a graph.

     

    Thanks!

    Sunday, March 09, 2008 12:42 AM
  • I'm having a new question (problem) with this topic and thought it made more sense to continue the thread rather than start a new one.

     

    I have a set of entity object graphs that I get as a result of a query with an Include. For simplicity, let's say I'm getting Contacts with an Address EntityRef.

     

    I call ToList on the query and put the list of objects in Session state of a web page.

     

    When the web page posts back, I pull the list of entity objects out of session.

     

    Because I never really detached them from the original context, I disconnect from the changeTracker before I attach again:

     

    For Each cachedContact In ContactList

    ' remove reference to original context

      Dim conChangeTracker As System.Data.Objects.DataClasses.IEntityWithChangeTracker = cachedContact

      conChangeTracker .SetChangeTracker(Nothing)

      baContext.Attach(cachedContact )

    Next

     

    But I get this error:

    An entity object cannot be referenced by multiple instances of IEntityChangeTracker

     

    The error is not coming from the Contact but the Address. If I remove the Include from the original query, it works fine.

     

    I thought that this method would detach the entire graph intact. The navigation property for Address *is* there. SO the graph is in fact, in tact (uh oh, shades of Dr. Seuss?). So what is actually going on here? And how can I correct it?

     

    I don't want to have to add more code to this solution. Can you suggest a cleaner way?

     

    Thanks.

     

    Julie

    Sunday, April 27, 2008 10:39 PM
  • The more I thought about it, the more I realized the answer was in my question. (Not an uncommon thing!)  I needed to also setentitychangeTracker(nothing) on Contact.Address.

     

    An extension method is perfect for this:

     

    <Extension()> _

    Public Sub RemoveChangeTracker(ByRef entObject As System.Data.Objects.DataClasses.IEntityWithChangeTracker)

       entObject.SetChangeTracker(Nothing)

    End Sub

    Sunday, April 27, 2008 11:59 PM
  • While this may well have helped work around the problem you are dealing with, I would suggest being very careful with this kind of technique.  Detach does more than just clear the tracker.  If you just clear the tracker, then you have at least paritally corrupted the context / state manager data structures. As I say, in some cases it will work--particularly if you are destroying the context--but it's generally not what I would recommend.

     

    - Danny

    Monday, April 28, 2008 12:26 AM
  •  

    Thanks for the warning  and further details (and you know I'll pass it on).

     

    I'm definitely "cleaning up" after a disappearing objectContext. It's a postback on a webform where I have stored my entity objects in session and need to attach them to a new context. But there's nothing to detach from. And since there was a relationship involved, I wanted to get the whole graph "unhooked" and then just attach it as a whole.

     

    In this case if there is still a better way, definitely let me know.

     

    Monday, April 28, 2008 12:45 AM
  •  

    One more question about this. When putting objects into ASP.NET session, they are binary serialized automatically. I thought that serialization also detaches, but it seems not to (as in the case above). Well, I should restate that. I thought that serializing creates a copy of the entities and they are detached.

     

    I also notice that when I have a new page, with no objectcontext at all, then retrieve those entities, their state is a valid "attached" state - eg.unchanged.

     

    Is it binary serialization that doesn't detach? XML and WCF sure seems to.

     

    thanks

     

    julie

     

     

    Friday, August 01, 2008 1:35 AM
  • I'd need to see more of your code to tell what's really going on...  Serialization doesn't detach per se, but what it does do is create new objects which happen to have the same contents as your old objects.  These new objects will be detached.  It doesn't matter if they are serialized via binary serialization, xml, wcf or something else.

     

    If you are seeing things that are not detached, then either there is a bug that the state is being reported incorrectly or (my guess) you didn't really have serialization going on.  I haven't looked into it enough to tell, but it's my suspicion that with something like asp.net session state if you put something into session state and then get it right back out you may not have serialization going on.  The serialization would only happen if you put it in, and then had some kind of transition like rendering a page to the browser and then coming back on another request.  Is it possible you were putting it in and then getting it back out during the same request or something?

     

    - Danny

    Friday, August 01, 2008 3:38 AM
  •  

    It's a simple set up, but I"m testing on a postback, not on a complete refresh of the page.

     

    I'm creating a context in the form load (when ispostback=FALSE) and running a query, then executing it with ToList.

     

    On form unload , I'm putting the list into session state.

     

    On form load when ispostback = true, I'm grabbing the list out of session state. Looking at a random entity, the EntityState is Unchanged and if I try to attach I get the message indicating that it's already tied to a change tracker.

     

    I will do some more experiments this morning and test iwth a full refresh of the page.

     

    Either way, I'm going to recommend always detaching before putting into sessoin,but I'm also very interested in understanding the serialization behavior  on session as well.

     

    thanks

     

    julie

    Friday, August 01, 2008 11:18 AM
  •  

    Ahh - a 5 year old misunderstanding cleared up! :-)

     

    From an article by Dino E  (http://msdn.microsoft.com/en-us/library/aa479041.aspx#aspnetsessionstate_topic5)

     

    "When you use the in-process mode, objects are stored in the session state as live instances of respective classes. No real serialization and deserialization ever takes place, meaning that you can actually store in Session whatever objects you have created (including nonserializable objects and COM objects) and access them with no significant overhead. The situation is different if you opt for an out-of-process state provider."

     

    That means being even more diligent to warn people away from attempting to stuff objectContext into session. I thought it wasn't even possible because it wasn't serializable. Yikes. There are lots of opportunities to get yourself in big trouble with the network admin!

    Friday, August 01, 2008 11:59 AM
  • Hi

     

    any solution to this problem? I having similar problem as well.

     

    Below is my code

     

    Code Snippet

    Dim issueDA As IssueDataAccess = New IssueDataAccess
            Dim _issue As New Issue

            Dim projectDA As New ProjectDataAccess
            Dim _project = projectDA.GetProjectById(29)

            _issue.Id = Guid.NewGuid
            _issue.Title = "test"
            _issue.Description = "test"
            _issue.LongDescription = "long"
            _issue.CreationDate = Date.Today
            _issue.CreatorId = "test"
            _issue.IssueTracker_Projects = _project

            issueDA.Insert(_issue)

     

     

     

     

     

     

    Wednesday, September 10, 2008 9:30 AM
  • There were a lot of things discussed in this thread. What are you having a similar problem to with this code?

     

     

    julie

    Wednesday, September 10, 2008 11:23 AM
  •  Patrick Yong wrote:

    Hi

     

    any solution to this problem? I having similar problem as well.

     

    Below is my code

     

    Code Snippet

    Dim issueDA As IssueDataAccess = New IssueDataAccess
            Dim _issue As New Issue

            Dim projectDA As New ProjectDataAccess
            Dim _project = projectDA.GetProjectById(29)

            _issue.Id = Guid.NewGuid
            _issue.Title = "test"
            _issue.Description = "test"
            _issue.LongDescription = "long"
            _issue.CreationDate = Date.Today
            _issue.CreatorId = "test"
            _issue.IssueTracker_Projects = _project

            issueDA.Insert(_issue)

     

     

     

     

     

     

     

    Hi Patrick,

     

    I was having the same issue the only solution that I came up it's with this:

     

    Dim issueDA As IssueDataAccess = New IssueDataAccess
            Dim _issue As New Issue

            Dim projectDA As New ProjectDataAccess
            Dim _project = ------> HERE YOUR LINQ

            _issue.Id = Guid.NewGuid
            _issue.Title = "test"
            _issue.Description = "test"
            _issue.LongDescription = "long"
            _issue.CreationDate = Date.Today
            _issue.CreatorId = "test"
            _issue.IssueTracker_Projects = _project

            issueDA.Insert(_issue)

     

     

    It's not very elegant but I had to finish something very fast, with time I'll make a better solution.

     

    Sorry for my english Stick out tongue

     

     

    Tuesday, September 16, 2008 6:30 PM
  • I have created a class that I think quite nicely solves this problem. You can read about it here:

     

    http://architectmuse.blogspot.com/2008/09/code-bits-scoped-objectcontexts.html

    Tuesday, September 16, 2008 6:36 PM
  •  

    What does this mean for garbage collection?

     

    Considering you are disposing your object context  (without overriding IDisposable)  and your objects remain in attached state? Does GC occur here?

     

    Grtz

    Tuesday, September 23, 2008 4:09 PM
  • ObjectSpace doesn't change what happens with ObjectContext when it is disposed of. If you wish to implement a detach operation on the contect during its disposal, then you would need to override that in each of your contexts...ObjectSpace is just a wrapper around ObjectContext that allows implicit "passing" from one call to the next. Any changes to the IDisposable implementation on your ObjectContext will function the same with or without using ObjectSpace.

     

    Tuesday, September 23, 2008 4:22 PM
  • This technique may work. But what if an object to be detached have some related child objects that have to be detached as well. You do not want to traverse the entire object hierarchy and in some cases the depth of this hierarchy is not known at run-time. How do we detach these hierarchical objects? Without this the framework becomes handicaped. Hibernate perfomes all this things easily.

     

    Thanks

    Simon.

     

    Wednesday, November 12, 2008 9:51 PM
  • Right now if you want to detach things from an object context you have to manually walk the graph and detach everything you want.  The egeneral expectation is that most of the time you would keep the context around as long as the entity instances are relevant and then dispose of the context and its entities all at once.

     

    For those cases where you do want instances to live longer than the context, then detaching is your option, but in the first release of the EF that detaching is unfortunately restricted to a single entity at a time.

     

    We understand the need to have the ability to detach a graph of related entities all at once, and it is on the backlog, but I'm not yet sure when that will show up.

     

    - Danny

    Wednesday, November 12, 2008 9:55 PM

  • Hi Jeff,
    I'm new to EF. I get the "An entity object cannot be referenced by multiple instances of IEntityChangeTracker" exception, when trying to perform a "Context.AddToEntity(Entity).

    I assume your solution ("In the mean time, ....") should help with this, but I can't figure out what exactly I'm supposed to be setting explicitly to null. Can you please provide more detail about this workaround?

    thanks

    -pH
    Sunday, September 13, 2009 4:44 AM

  • Jeff, in .net 4.0 does object context when disposed detaches the object? u said their is active work item. Can u tell me if their is any work done in this area?

    Zeeshan
    Friday, September 25, 2009 7:10 PM