Entity framework uses a lot of memory

17,756

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 doing from 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.

Share:
17,756
Naor
Author by

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, 2022

Comments

  • Naor
    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?

    ANTS memory profiler

    **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<>));
            });