Entity framework consumes a significant amount of memory.

.net c# entity-framework linq-to-entities performance

Question

An picture from the ANTS memory profiler is seen here. There are evidently many items stored in memory. How can I identify my mistakes?

ANTS memory profiler

**UPDATE**

My repository classes are listed below:

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 class A already exists and it contains data from table A, I also construct class:

public class ARepository:BaseRepository<A> {
// Implementation of A's queries and specific db operations
}

My EFUnitOfWork class is shown below:

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

Further, 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's how I apply it:

 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'm hoping that's enough information for me to solve the issue. Thanks.

UPDATE 2
UnitOfWorkFactory is another option for initializing 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);
    }
}

I utilize structuremap at the application setup in order to use this:

  ObjectFactory.Initialize(x =>
        {
            x.For<IUnitOfWorkFactory>().Use<UnitOfWorkFactory>();
            x.For(typeof(IRepository<>)).Use(typeof(Repository<>));
        });
1
13
10/8/2011 12:30:20 AM

Accepted Answer

I suspect you don't remove the context.
As soon as you are through dealing with the database, I advise disposing of the context.

Use using anytime you establish the context, a statement.

[Edit]

I observe that you store rather than discard yourEFUnitOfWork object. It is disposable, which is true, but I'm not sure when anything is considered disposable. It seems that throughout all phases of application execution, you have a reference to the context.
It will become worse since you generate and maintain one context per thread.

I am unable to provide you with specific placement advice.Dispose or using I'm not familiar with the usages, thus
You may likely apply it to yourCommit technique, but I'm not sure whether theCommit Throughout a database interaction session, just one call is made.

Additionally, your design may be very complex.

In your place, I would:

  • As a temporary fix, figure out how to dispose of the context using the present code.
  • As a long-term solution, make the design simpler.

I would implement a long-term remedy straight immediately if I had the time.
However, as I am unaware of the size, purpose, and needs of your application, I am unable to determine if the complexity of your design is warranted.

17
5/16/2011 4:48:03 PM

Popular Answer

I can think of a few things:

  • Most likely, you aren't disposing of the object context. Ensure that all database code is contained insideusing(var context = CreateObjectContext()) block
  • You are sending entities from the data access layer to the top layer in an N-tier design without detaching the entities from the object context. You must use the ObjectContext method. Detach(...)
  • Instead of returning a single entity for a single Get operation, you are most likely returning an entire collection of entities. You could ask questions likefrom customer in context.Customers select customer rather than doingfrom customer in context.Customers select customer.FirstOrDefault()

Entity Framework has been difficult for me to integrate into an N-tier application. Simply said, it shouldn't be used in N-tier applications as is. It's just in EF 4.0. You may read everything about my struggles to get EF 3 to function in an N-tier app.

http://www.codeproject.com/KB/linq/ef.aspx

Has this answered your inquiry?



Related Questions





Related

Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow
Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow