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.
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
Related
I am new to Entity Framework.
Working on an existing application that uses the repository pattern.
When the context was created in the get property (no constructor) we got sporadic errors relating to the DbContext in production and in load test
while it worked fine in local.
The issues got resolved when we added a constructor to the context class and initialized the context in the constructor.
It does the same thing as the getter.
Unsure has to how that solved the problem.
Anyone has an idea as to what is the difference between creating the context in the constructor vs getter that solved these issues.
I have searched the web high and low.
Below are the errors we encountered:
The connection was not closed. The connection's current state is connecting.
Not allowed to change the ConnectionString property. The connection's current state is closed.
System.NullReferenceException: Object reference not set to an instance of an object. (the object here is the context).
further exception: The context cannot be created while the model is being created. This exception may be thrown if the context is used inside the OnModelCreating method or if the same context instance is accessed by multiple threads concurrently. Note that instance members of Dbcontext and related classes are not guaranteed to be thread safe.
Below is the code:
EF DbContext
public partial class EFContext : DbContext, IContext
{
public EFContext(): base("ConnectionStringName")
{
}
public IDbSet<TEntity> entityname { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
//entity classes configuration
ConfigureEntity(modelBuilder);
}
}
MyContext
public partial class MyContext : IContext, IDisposable
{
#region Private Members
private bool _disposed;
private EFContext efContext = null;
public MyContext () //Constructor
{
if (HttpContext.Current != null)
{
string entityKey = "_" + HttpContext.Current.GetHashCode().ToString("x");
if (!HttpContext.Current.Items.Contains(entityKey))
{
efContext = new EFContext();
HttpContext.Current.Items.Add(entityKey, efContext);
}
}
else
{
efContext = new EFContext();
}
}
private EFContext Entities
{
get
{
if (HttpContext.Current != null)
{
string entityKey = "_" + HttpContext.Current.GetHashCode().ToString("x");
if (!HttpContext.Current.Items.Contains(entityKey))
{
efContext = new EFContext();
HttpContext.Current.Items.Add(entityKey, efContext);
}
return (EFContext)HttpContext.Current.Items[entityKey];
}
else
{
efContext = new EFContext();
return efContext;
}
}
}
}
Thank you in advance.
I am seeing a The operation cannot be completed because the DbContext has been disposed. exception message even though the code is using eager loading by returning List<T>. I have the following code in an EntityRepository class:
public List<Entity> GetEntityByProperty(string property)
{
using (AppDbContext appDbContext = GetContext())
{
List<Entity> entities = appDbContext.Entities
.Where(e => e.Property == property)
.ToList();
return entities;
}
}
This is in turn being called from a web service class:
private static EntityRepository EntityRepositoryStatic = new EntityRepository();
[WebMethod]
public List<Entity> GetEntityByProperty(string property)
{
return EntityRepositoryStatic.GetEntityByProperty(property);
}
The EntityRepository inherits from a Repository base class which implements IDisposable (to dispose the context) and has this code to return an instance of the context:
private AppDbContext appDbContext;
public AppDbContext GetContext()
{
if (appDbContext == null)
{
appDbContext = new AppDbContext();
}
return appDbContext;
}
The exception is thrown on the line making the call to appDbContext in the repository. Am I missing something obvious?
This is a large legacy codebase therefore we are simply trying to replace inline calls to the context in the web service with calls to the repository classes instead.
The first time you call GetContext() you create a new context, then you return it, then it's used and disposed of. Then the next call to GetContext() returns the original, already disposed, context, and you get an error. If you want to re-use it, you can't dispose it; if you want to dispose of it when done, you can't re-use it.
I have a very simple application using EF. But when it runs a week, the memory usage is awful (only 80MB at first, 700MB after one week).
When I use dotMemory to profile my application. I find the memory of Heap generation 2 is increasing all the time.
I Get a snapshot, finally find the retained bytes of ef dbcontext is the most.
I am so confused. My application is so simple. Code sample:
protected CarbonBrushMonitorEntities _entities = new MYEntities();
public void Add(HistoryData data)
{
_entities.HistoryDatas.Add(data);
_entities.SaveChanges();
}
_entities only initials once at the starting time, then used all the time.
The function Add is frequently called,about 3 times/second
I google a long time, and try some methods such as:
_entities.Configuration.ValidateOnSaveEnabled = false;
_entities.Configuration.AutoDetectChangesEnabled = false;
_entities.Configuration.LazyLoadingEnabled = false;
but these do not work.
If you use entity framework, you should create the context just before you need it and dispose it as soon as possible:
using (var someContext = new SomeContext())
{
// your commands/queries
}
Never keep context in memory or share it across different calls.
What I typically do is register the context with an IoC container:
DependencyFactory.RegisterType(typeof(SomeContext));
and use a context resolver (also registered with IoC of course) like:
using (var someContext = _contextResolver.ResolveContext())
{
// your commands/queries
}
where resolution is done like:
public class ContextResolver : IContextResolver
{
public ISomeContext ResolveContext()
{
return DependencyFactory.Resolve<SomeContext>();
}
}
The EF context is actually your unit of work, which should be disposed of once you don't need it anymore.
The other way is to clear the changetracker of the respective entities of concern or even
of all the entities. This is done by changing the entity state to "Detached". This called after dbContext.SaveChangesAsync()
protected void DisposeDbset<T>() where T : class
{
var Tname = typeof(T).Name;
var changetrackercollection = _unitOfWork.dbContext.ChangeTracker.Entries<T>();
foreach (var item in changetrackercollection.ToList())
{
item.State = EntityState.Detached;
}
GC.Collect();
}
I recently faced a similar situation where I was inserting 3,00,000 rows in batch operation. After inserting the rows, the change tracking info for all the rows remained in the memory with the entity state as Unchanged. Hence after every SaveChangesAsync() call, the changetracker accumulated.
I could not resolve new instance dbcontext for every batch, as it was a more expensive operation.
Just FYI, i had configured dbConetext.ChangeTracker.QueryTrackingBehavior = NoTracking. But this is applicable to while fetching the data.
Hopefully this is helpful. I found my solution with the help of this link http://andreyzavadskiy.com/2016/09/23/entries-in-entity-framework-changetracker-could-degrade-database-write-performance/?unapproved=19301&moderation-hash=4acc61a32ead7232959c2ec1ca268180#comment-19301
Based on Chintan shah answer I made an extension method and an example.
public static class DbContextExtensions
{
/// <summary>
/// Set all entries in ChangeTracker to detached to get them collected by the GC
/// </summary>
/// <param name="context"></param>
public static void DetachAllEntriesInChangeTracker(this DbContext context)
{
try
{
foreach (var entityEntry in context.ChangeTracker.Entries())
{
entityEntry.State = EntityState.Detached;
}
}
catch (Exception e)
{
LogManager.GetLogger(context.GetType().FullName).Error(e, "error when detaching all entries in changeTracker");
}
}
}
public class FooDbContext : DbContext
{
public override void Dispose()
{
this.DetachAllEntriesInChangeTracker();
base.Dispose();
}
}
We create one Linq2Sql DataContext for every request using the following class:
public static class DataContextManager
{
private const string HTTPCONTEXT_KEY = "DataContextManagerKey";
private static CompanyDataContext _staticContext; //when there's no HttpContext (in test/debug situations).
public static CompanyDataContext Context
{
get
{
if (_Context == null)
{
_Context = NewContext();
}
return _Context;
}
}
private static CompanyDataContext _Context
{
get
{
return (CompanyDataContext)(HttpContext.Current != null ? HttpContext.Current.Items[HTTPCONTEXT_KEY] : _staticContext);
}
set
{
if (HttpContext.Current != null)
{
HttpContext.Current.Items[HTTPCONTEXT_KEY] = value;
}
else
{
DataContextManager._staticContext = value;
}
}
}
public static void Dispose()
{
CompanyDataContext context = _Context;
if (context != null)
{
if (Config.Instance.TestMode) context.Log.WriteLine("--- DISPOSING DATACONTEXT ---");
context.Dispose();
_Context = null;
}
}
public static CompanyDataContext NewContext()
{
CompanyDataContext db = new CompanyDataContext();
db.CommandTimeout = Config.SQL_COMMAND_TIMEOUT;
if (Config.Instance.TestMode)
{
db.Log = new ConsoleTextWriter();
db.Log.WriteLine("--- CREATING NEW DATACONTEXT ---");
}
return db;
}
}
And in Global.asax:
protected void Application_EndRequest(Object sender, EventArgs e)
{
DataContextManager.Dispose();
}
The reason why I'm asking is that we're suddenly getting random "SqlException: Server failed to resume the transaction" exceptions once or twice every day with code that used to work perfectly. After the exception we get a lot of other exceptions until we restart the web application. Anyone seen this behaviour before?
We're running ASP .Net 2.0 with SQL server 2005 on IIS 6.
UPDATE:
Just so no one else does the same horrible mistake as we did:
It turned out that some worker threads also used the DataContext but with no HttpContext they of course got the _staticContext (a feature in DataContextManager only to be used when testing). We rewrote the code in the worker threads to make sure one DataContext per thread and disposing it when done. And so far everything has worked for 2 weeks :)
This is a bad pattern. First you should never have a static data context it implements IDisposable so one thread could try to use the context while another is disposing of it plus many other potential problems. One data context per http request is no good either, data contexts are designed to be used for a single transaction then disposed of. You get problems if you retrieve update/insert/delete and retrieve using the same context, the second retrieve does not reflect the changes of the update/insert/delete. Remove the static context and just have the Context property return a new context every time. You could still dispose of the all at the end of a request by sticking them all in a List property and iterating through it.
I am using ASP.Net / WebForms / Entity Model / Framework 3.5
Here is my project's simple structure
Forms > BLL > DAL ( uses entity model )
Here is my DAL's snippet
public class MyDAL : IDisposable
{
private MyEntities db;
public BaseDAL()
{
db = new MyEntities();
}
public User GetUserByID(int userId)
{
try
{
IQueryable<User> objUser = null;
objUser = from res in db.Users
where res.UserId == userId
select res;
return objUser.FirstOrDefault();
}
catch
{
throw;
}
}
public void Dispose()
{
db.Dispose();
}
}
I call the DAL's function from my BLL like this
public class MyBLL
{
public User GetUserByID(int userId)
{
try
{
using (MyDAL objMyDAL = new MyDAL())
{
return objMyDAL.GetUserByID(userId);
}
}
catch
{
throw;
}
}
}
I am calling the DAL through using block so MyDAL's Dispose event will fire soon after the BLL returns the User object. So at this point ObjectContext instance gets disposed.
Now in my Web Form, I am calling this function like this to get user information and Group details which is a foreign key of user_Group Table in User table
protected void Page_Load(object sender, EventArgs e)
{
MyBLL objMyBll = new MyBLL();
User objUser = objMyBll.GetUserByID(123);
objUser.User_GroupReference.Load(); // ERROR LINE
int groupId = objUser.User_Group.Group_Id;
}
When the ode comes on line objUser.User_GroupReference.Load(); I get this exception
The ObjectContext instance has been disposed and can no longer be used
for operations that require a connection.
How to resolve this? If I do not do db.Dispose(); in my DAL's dispose method, it works fine and no exceptions comes. But if I do not dispose the db object there, when & where should I dispose it?
And how to access Reference Keys after disposed object context?
The exception is fired because lazy loading is fired when you access that navigation property but lazy loading works only within scope of context used to load the entity. If you dispose the context you will lose lazy loading ability. There is no way to use lazy loading after context disposal (except attaching the entity to the new context but it will only work if you detach it from the original context before you dispose it).
In your architecture you must use Include to explicitly load every relation you will need in upper layers. If you want to use lazy loading your context must live for the whole duration of the request. In case of web forms it can be handled for example in BeginRequest and EndRequest event handlers where you create context in BeginRequest and dispose it in EndRequest. The context will be stored in HttpContext.Items. You should get the context from this collection (you can make helper method for that) and pass it to constructor of BLL which will in turn pass it to DAL. Don't access HttpContext.Items from BLL or DAL.