C# Entity Framework using only one ObjectContext per HttpContext
In ASP.NET MVC 2, using Entity Framework 4, I'm getting this error "An entity object cannot be referenced by multiple instances of IEntityChangeTracker".
A search of SO shows that it is probably because I have different instances of the Entity Framework ObjectContext, when it should be only one ObjectContext instance for each HttpContext开发者_JAVA百科.
I have this code (written long before I joined) that appears to do just that - have one ObjectContext for every HttpContext. But I am getting the "IEntityChangeTracker" exception frequently so it is probably not working as intended:
// in ObjectContextManager.cs
public const string ConnectionString = "name=MyAppEntities";
public const string ContainerName = "MyAppEntities";
public static ObjectContext GetObjectContext()
{
ObjectContext objectContext = GetCurrentObjectContext();
if (objectContext == null) // create and store the object context
{
objectContext = new ObjectContext(ConnectionString, ContainerName);
objectContext.ContextOptions.LazyLoadingEnabled = true;
StoreCurrentObjectContext(objectContext);
}
return objectContext;
}
private static void StoreCurrentObjectContext(ObjectContext objectContext)
{
if (HttpContext.Current.Items.Contains("EF.ObjectContext"))
HttpContext.Current.Items["EF.ObjectContext"] = objectContext;
else
HttpContext.Current.Items.Add("EF.ObjectContext", objectContext);
}
private static ObjectContext GetCurrentObjectContext()
{
ObjectContext objectContext = null;
if (HttpContext.Current.Items.Contains("EF.ObjectContext")
objectContext = (ObjectContext)HttpContext.Current.Items["EF.ObjectContext"];
return objectContext;
}
I've examined this code and it looks correct. It does as far as I can tell return one ObjectContext instance for each HttpContext. Is the code wrong?
If the code is not wrong, why else would I get the "An entity object cannot be referenced by multiple instances of IEntityChangeTracker" exception?
EDIT: To show how the ObjectContext is disposed:
// in HttpRequestModule.cs
private void Application_EndRequest(object source, EventArgs e)
{
ServiceLocator.Current.GetInstance<IRepositoryContext>().Terminate();
}
// in RepositoryContext.cs
public void Terminate()
{
ObjectContextManager.RemoveCurrentObjectContext();
}
// in ObjectContextManager.cs
public static void RemoveCurrentObjectContext()
{
ObjectContext objectContext = GetCurrentObjectContext();
if (objectContext != null)
{
HttpContext.Current.Items.Remove("EF.ObjectContext");
objectContext.Dispose();
}
}
My guess is that you've stored an object somewhere in memory (most likely the http cache using in-process mode, but could also be any manual cache such as a shared dictionary), and now you've somehow associated that object with something else, for example:
newOrder.OwnerUser = currentUser; // <== let's say currentUser came from cache
// and newOrder was on your new entity context
Hence, a problem if the cached object still thinks it is attached to a context; not least, you are probably keeping an entire graph alive accidentally.
The code looks OK (as long as you are disposing it at the end of the request), but this would be a good time to add:
private const string EFContextKey = "EF.ObjectContext";
and use that in place of the 5 literals. Avoids a few risks ;p
精彩评论