Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Entity framework uses a lot of memory

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<>));
        });
like image 515
Naor Avatar asked May 15 '11 18:05

Naor


People also ask

Is entity framework good for large database?

It is very possible to do very complex joins on multiple tables with EF in SQL, however it can take some experience to avoid some accidental in memory joins. Entity Linq has a relatively shallow learning curve, but because of this, also has many gotchas.

Is Entity Framework core faster than Entity Framework?

The conclusions are obvious: in almost every test conducted by Chad, Entity Framework Core 3 is faster than Entity Framework 6 – exactly 2.25 to 4.15 times faster! So if performance is important to your application and it operates on large amounts of data, EF Core should be a natural choice.

What is the advantage and disadvantage of entity framework?

Easy to map business objects (with drag & drop tables on environment). It keeps a good performance when you work with a small / middle domain model. Disadvantages: You have to think in a non-traditional way of handling data , not available for every database.

Is entity framework worth using?

Conclusion. EF should be considered a great ORM framework which allows faster development, easier and quicker operations to the DB, as long as you are careful and know how it works in order to avoid certain mistakes and create performance problems.


Video Answer


2 Answers

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.

like image 68
Alex Aza Avatar answered Sep 19 '22 08:09

Alex Aza


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?

like image 42
oazabir Avatar answered Sep 20 '22 08:09

oazabir