Note that there are some explanatory texts on larger screens.

plurals
  1. POHow do I find the source of all the objects that are being tracked for changes in EF CodeFirst CTP5?
    text
    copied!<p>I'm having the 'An entity object cannot be referenced by multiple instances of IEntityChangeTracker' problem. After some checking around it seems like I have an object that's being tracked for changes. The problem is that I don't know the source of the problem object... it's obviously been put into the context, but I'm not sure which call hasn't been properly Detached.</p> <p>So, after hours of trying to figure this out, I'm looking for how to walk the tree to find the source object that I'm having the conflict with, as maybe that will help me understand where the source object is being Added.</p> <p>The error is being thrown on line 226, so it looks like I either have a 'stealth' Customer existing, or maybe one of the properties of Customer is causing this, as Customer has a couple other properties that are their own complex object types...</p> <pre><code>Line 224: if (null != this.Customer) Line 225: { Line 226: context.Entry(this.Customer).State = EntityState.Unchanged; Line 227: } </code></pre> <p>The error doesn't say <em>which</em> object is causing the error, it just points at line 226. Upon <strong>assuming</strong> it's a phantom Customer object that's causing this, I've tried:</p> <pre><code> var test = ((IObjectContextAdapter)dataContext).ObjectContext.ObjectStateManager.GetObjectStateEntries(EntityState.Added | EntityState.Deleted | EntityState.Modified | EntityState.Unchanged); foreach(var e in test) { if(e.GetType() == typeof(Customer)) { dataContext.Detach(e); } } </code></pre> <p>The idea was to loop through the thing that holds references to all the objects, hopefully find the naughty Customer and give it the boot. But, alas, this didn't work; no Customers are found in this loop. Oh, FYI - this is run a few lines before the previous code so I'm not sneaking in any extra object creation there.</p> <p>So I need a way to determine which object is in fact causing the error.</p> <p>@Ladislav - FYI - I have a common library that contains all the business objects (BO). This common library is used by other projects - Windows Service, Web Service, etc. I've tried to make each BO responsible for populating and saving itself, so that I don't have one hugo data access class. Each BO is responsible for it's own Save() method. Here's an example of a <strong>current</strong> saveUpdate method:</p> <pre><code> public void SaveOrUpdate(DataContext context) { if (context.Entry(this).State == EntityState.Detached) { context.Customers.Add(this); context.SaveChanges(); } else //update { context.Entry(this).State = System.Data.EntityState.Modified; context.SaveChanges(); } } </code></pre> <p>In regards to your suggestion of scope, I've tried various strategies - At first blush everyone says do it atomically - so I had each method grabbing a new Instance of the DataContext to do it's work. This worked fine, as long as the objects weren't very complex, and didn't depend on each other, i.e. only contained base type properties like int and string.</p> <p>But once I started getting these concurrency errors, I dug into it and found out that the DataContext somehow held on to references to objects <strong>even when it was disposed of</strong> That's a bit of a crazy-bad piece of engineering, IMHO. i.e. So if I add a Customer BO to the DataContext then allow the DataContext to go out of scope and be Disposed, and then spin up a <strong>new</strong> DataContext to do something, the original Customer BO pointer is still there!</p> <p>So I read a bunch on StackOverflow (with many answers by you, I might add), <a href="http://www.west-wind.com/weblog/posts/246222.aspx" rel="nofollow">Rick Strahl's treatise on DataContext Lifetime Management</a> and the <a href="http://www.code-magazine.com/articleprint.aspx?quickid=0907071&amp;printmode=true" rel="nofollow">8 Entity Framework Gotchas by Julia Lerman</a></p> <p>So Julia says put in a Dispose method, and I did, but it didn't help, the DataContext is still magically holding onto the reference.</p> <p>So Rick says try to use a 'global' DataContext, so that you only have one DataContext to worry about, and it should know everything that's going on, so it doesn't step on it's own toes. But that didn't seem to work either. To be fair, Rick is talking about Linq to SQL, and a web app, but I was kinda hoping it would apply to me too.</p> <p>And then various answers say that you <strong>Don't</strong> want a Global DataContext, since it's going to get very big, very quickly, since it's holding all the info about all your objects, so just use DataContext for a <em>Unit Of Work</em>.</p> <p>Well, I've broken down a <em>Unit of Work</em> to mean all changes, additions and updates done to a group of objects that you'd like done together. So for my Example Here are some BOs and properties:</p> <p><strong>MessageGroup</strong><br> - Property: List<br> - Property: Customer </p> <p><strong>Customer</strong><br> - Property: List<br> - Property: List </p> <p><strong>Message</strong><br> - Property: Customer<br> - Property: MessageGroup<br> - Property: User </p> <p><strong>User</strong><br> - Property: Customer<br> - Property: List </p> <p>In the system when a MessageGroup arrives (as Xml), it's examined and parsed. The MessageGroup constructor used Dependency Injection and takes the DataContext as one of it's parameters - so all the 'child' BOs being created are using this one instance of the DataContext. The Customer is fetched from the database (or a new one is created) and assigned to the MessageGroup... let's assume it's an <em>existing</em> Customer - so no updates need to be done to it, it's fresh out of the DataContext.</p> <p>Then the MessageGroup.Messages list is looped and the first Child BO to create is a new User object. I assign the same Customer object (from the MessageGroup) to the User. However, when context.Users.Add(this) is called, I get the error. If I don't assign the Customer to the User, I don't get the error.</p> <p>So now I have a Customer (or a child property, I'm not sure) that's fresh from the DB, that I don't need tracked causing me angst. I thought I could just remove it from the context by using something like:</p> <pre><code>var cust = Customer.GetCustomerFromExternalId(crm.CustomerId); dataContext.Detach(cust); dataContext.SaveChanges(); </code></pre> <p>But I still get the error, even though I've explicity removed it. Of course if it's one of the child properties of Customer, maybe that hasn't been removed?</p> <p>Currently I'm wondering if the Repository Pattern is suitable for my purpose. I'm also wondering if EF CodeFirst is fundamentally flawed or just overly complex? Maybe I should use SubSonic or NHibernate instead?</p>
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload