Entity framework uses a lot of memory
Solution 1
I have a hunch you don't dispose the context.
I suggest disposing the context whenever you done interacting with database.
Use using
statement whenever you create the context.
[Edit]
As far as I can see, you cache and don't dispose your EFUnitOfWork
object. It is disposable, which is correct, but I don't see when disposable is called. Seems like you hold a reference to the context for all application run time.
Moreover, you create and hold one context per thread, which will make it even worse.
I can't tell you for sure where you should put Dispose
or using
, as I don't know the usages.
You could put it probably to your Commit
method, but I don't know if the Commit
called only once during database interaction session.
Also, your design might be overcomplicated.
If I were you, I would:
- Find the way to dispose the context using current code, as a short-term solution
- Simplify the design, as the long-term solution
If I had time I would do long-term solution right away.
But again, I can't tell if the complexity of your design is justified, as I don't know how big your application is and what it does and what the requirements are.
Solution 2
Couple of things come to my mind:
- You aren't probably Disposing the ObjectContext. Make sure all your database codes are within
using(var context = CreateObjectContext())
block - You have an N-tier architecture and you are passing entities from the data access layer to upper layer without Detaching the entities from ObjectContext. You need to call ObjectContext.Detach(...)
- You are most likely returning a full collection of entities, instead of returning a single enity for single Get operations. For ex, you have queries like
from customer in context.Customers select customer
instead of doingfrom customer in context.Customers select customer.FirstOrDefault()
I have had hard time making Entity Framework to work in an N-tier application. It's just not suitable for using in N-tier apps as is. Only EF 4.0 is. You can read about all my adventure in making EF 3 work in an N-tier app.
http://www.codeproject.com/KB/linq/ef.aspx
Does this answer your question?
Solution 3
Do you clear the ObjectContext
once in a while. If you keep an ObjectContext
alive for a long time this will consume memory related to the size of the EntityDataModel and the number of Entities loaded into this ObjectContext
.
Naor
My name is Naor and I live in the holy city Jerusalem of Israel. I am 31 years old, married to my lovely Ilana and we have a female cat named Mutzi. I am working as web developer and I create web apps in my free time.
Updated on June 04, 2022Comments
-
Naor almost 2 years
Here is a image from the ANTS memory profiler. It seens that there are a lot of objects hold in memory. How can I find what I am doing wrong?
**UPDATE**
Here is my repository classes:
public class Repository<T> : IRepository<T> where T : class, IDataEntity { ObjectContext _context; IObjectSet<T> _objectSet; readonly string _entitySetName; readonly string[] _keyNames; private ObjectContext Context { get { if (_context == null) { _context = GetCurrentUnitOfWork<EFUnitOfWork>().Context; } return _context; } } private IObjectSet<T> ObjectSet { get { if (_objectSet == null) { _objectSet = this.Context.CreateObjectSet<T>(); } return _objectSet; } } public TUnitOfWork GetCurrentUnitOfWork<TUnitOfWork>() where TUnitOfWork : IUnitOfWork { return (TUnitOfWork)UnitOfWork.Current; } public virtual IEnumerable<T> GetQuery() { return ObjectSet; } public virtual IEnumerable<T> GetQuery(params Expression<Func<T, object>>[] includes) { return ObjectSet.IncludeMultiple(includes); } public virtual IEnumerable<T> GetQuery( IEnumerable<Expression<Func<T, bool>>> filters, Func<IQueryable<T>, IOrderedQueryable<T>> orderBy, IEnumerable<Expression<Func<T, object>>> includes) { IQueryable<T> _query = ObjectSet; if (filters != null) { foreach (var filter in filters) { _query = _query.Where(filter); } } if (includes != null && includes.Count() > 0) { _query = _query.IncludeMultiple(includes.ToArray()); } if (orderBy != null) { _query = orderBy(_query); } return _query; } public virtual IPaged<T> GetQuery( IEnumerable<Expression<Func<T, bool>>> filters, Func<IQueryable<T>, IOrderedQueryable<T>> orderBy, int pageNumber, int pageSize, IEnumerable<Expression<Func<T, object>>> includes) { IQueryable<T> _query = ObjectSet; if (filters != null) { foreach (var filter in filters) { _query = _query.Where(filter); } } if (orderBy != null) { _query = orderBy(_query); } IPaged<T> page = new Paged<T>(_query, pageNumber, pageSize, includes); return page; } public virtual void Insert(T entity) { this.ObjectSet.AddObject(entity); } public virtual void Delete(T entity) { if (entity is ISoftDeletable) { ((ISoftDeletable)entity).IsDeleted = true; //Update(entity); } else { this.ObjectSet.DeleteObject(entity); } } public virtual void Attach(T entity) { ObjectStateEntry entry = null; if (this.Context.ObjectStateManager.TryGetObjectStateEntry(entity, out entry) == false) { this.ObjectSet.Attach(entity); } } public virtual void Detach(T entity) { ObjectStateEntry entry = null; if (this.Context.ObjectStateManager.TryGetObjectStateEntry(entity, out entry) == true) { this.ObjectSet.Detach(entity); } } }
Now, if I have class A that holds records from table A, I also create class:
public class ARepository:BaseRepository<A> { // Implementation of A's queries and specific db operations }
Here is my EFUnitOfWork class:
public class EFUnitOfWork : IUnitOfWork, IDisposable { public ObjectContext Context { get; private set; } public EFUnitOfWork(ObjectContext context) { Context = context; context.ContextOptions.LazyLoadingEnabled = true; } public void Commit() { Context.SaveChanges(); } public void Dispose() { if (Context != null) { Context.Dispose(); } GC.SuppressFinalize(this); } }
And UnitOfWork class:
public static class UnitOfWork { private const string HTTPCONTEXTKEY = "MyProj.Domain.Business.Repository.HttpContext.Key"; private static IUnitOfWorkFactory _unitOfWorkFactory; private static readonly Hashtable _threads = new Hashtable(); public static void Commit() { IUnitOfWork unitOfWork = GetUnitOfWork(); if (unitOfWork != null) { unitOfWork.Commit(); } } public static IUnitOfWork Current { get { IUnitOfWork unitOfWork = GetUnitOfWork(); if (unitOfWork == null) { _unitOfWorkFactory = ObjectFactory.GetInstance<IUnitOfWorkFactory>(); unitOfWork = _unitOfWorkFactory.Create(); SaveUnitOfWork(unitOfWork); } return unitOfWork; } } private static IUnitOfWork GetUnitOfWork() { if (HttpContext.Current != null) { if (HttpContext.Current.Items.Contains(HTTPCONTEXTKEY)) { return (IUnitOfWork)HttpContext.Current.Items[HTTPCONTEXTKEY]; } return null; } else { Thread thread = Thread.CurrentThread; if (string.IsNullOrEmpty(thread.Name)) { thread.Name = Guid.NewGuid().ToString(); return null; } else { lock (_threads.SyncRoot) { return (IUnitOfWork)_threads[Thread.CurrentThread.Name]; } } } } private static void SaveUnitOfWork(IUnitOfWork unitOfWork) { if (HttpContext.Current != null) { HttpContext.Current.Items[HTTPCONTEXTKEY] = unitOfWork; } else { lock(_threads.SyncRoot) { _threads[Thread.CurrentThread.Name] = unitOfWork; } } } }
Here is how I use this:
public class TaskPriceRepository : BaseRepository<TaskPrice> { public void Set(TaskPrice entity) { TaskPrice taskPrice = GetQuery().SingleOrDefault(x => x.TaskId == entity.TaskId); if (taskPrice != null) { CommonUtils.CopyObject<TaskPrice>(entity, ref taskPrice); } else { this.Insert(entity); } } } public class BranchRepository : BaseRepository<Branch> { public IList<Branch> GetBranchesList(Guid companyId, long? branchId, string branchName) { return Repository.GetQuery(). Where(b => companyId == b.CompanyId). Where(b => b.IsDeleted == false). Where(b => !branchId.HasValue || b.BranchId.Equals(branchId.Value)). Where(b => branchName == null || b.BranchName.Contains(branchName)). ToList(); } } [WebMethod] public void SetTaskPrice(TaskPriceDTO taskPrice) { TaskPrice tp = taskPrice.ToEntity(); TaskPriceRepository rep = new TaskPriceRepository(); rep.Set(tp); UnitOfWork.Commit(); } [WebMethod] public IList<Branch> GetBranchesList() { BranchRepository rep = new BranchRepository(); return rep.GetBranchesList(m_User.UserCompany.CompanyId, null, null).ToList(); }
I hope this is enough info to help me solving the problem. Thanks.
UPDATE 2
There is also UnitOfWorkFactory that initializes UnitOfWork:public class UnitOfWorkFactory : IUnitOfWorkFactory { private static Func<ObjectContext> _objectContextDelegate; private static readonly Object _lockObject = new object(); public static void SetObjectContext(Func<ObjectContext> objectContextDelegate) { _objectContextDelegate = objectContextDelegate; } public IUnitOfWork Create() { ObjectContext context; lock (_lockObject) { context = _objectContextDelegate(); } return new EFUnitOfWork(context); } }
In order to use this, in the application startup I use structuremap:
ObjectFactory.Initialize(x => { x.For<IUnitOfWorkFactory>().Use<UnitOfWorkFactory>(); x.For(typeof(IRepository<>)).Use(typeof(Repository<>)); });