实体框架使用了大量内存

时间:2011-05-15 18:11:25

标签: c# .net performance entity-framework linq-to-entities

这是来自ANTS内存分析器的图像。它认为内存中有很多对象。我怎样才能找到我做错的事?

ANTS memory profiler

**UPDATE**

这是我的存储库类:

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

现在,如果我有A类来保存表A中的记录,我也创建了类:

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

这是我的EFUnitOfWork课程:

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

和UnitOfWork类:

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

以下是我如何使用它:

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

我希望这足以帮助我解决问题。感谢。

UPDATE 2
还有UnitOfWorkFactory初始化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);
    }
}

为了使用它,在应用程序启动时我使用了structuremap:

  ObjectFactory.Initialize(x =>
        {
            x.For<IUnitOfWorkFactory>().Use<UnitOfWorkFactory>();
            x.For(typeof(IRepository<>)).Use(typeof(Repository<>));
        });

4 个答案:

答案 0 :(得分:14)

我有预感你没有处理上下文 我建议你在与数据库交互时处理上下文。

创建上下文时使用using语句。

<强> [编辑]

据我所见,您缓存并且不会处置您的EFUnitOfWork对象。它是一次性的,这是正确的,但我没有看到什么时候被称为一次性。似乎你对所有应用程序运行时的上下文都有一个引用。
而且,你为每个线程创建并保存一个上下文,这会使它更糟。

我无法确定您应该将Disposeusing放在哪里,因为我不知道这些用法。
您可以将它放在Commit方法中,但我不知道Commit在数据库交互会话期间是否只调用过一次。

此外,您的设计可能过于复杂。

如果我是你,我会:

  • 找出使用当前代码处理上下文的方法,作为短期解决方案
  • 简化设计,作为长期解决方案

如果我有时间,我会立即做出长期解决方案 但同样,我无法判断您的设计是否合理,因为我不知道您的应用程序有多大,它的功能和要求是什么。

答案 1 :(得分:5)

我想到了一些事情:

  • 您可能不会处理ObjectContext。确保所有数据库代码都在using(var context = CreateObjectContext())
  • 您有一个N层架构,您正在将实体从数据访问层传递到上层而不从ObjectContext中分离实体。您需要调用ObjectContext.Detach(...)
  • 您最有可能返回完整的实体集合,而不是为单个Get操作返回单个enity。例如,您有from customer in context.Customers select customer之类的查询,而不是from customer in context.Customers select customer.FirstOrDefault()

我很难让Entity Framework在N层应用程序中工作。它不适合在N层应用程序中使用。只有EF 4.0。您可以阅读我在使用N层应用程序制作EF 3时的所有冒险经历。

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

这会回答你的问题吗?

答案 2 :(得分:3)

您偶尔会清除ObjectContext吗?如果长时间保持ObjectContext处于活动状态,则会占用与EntityDataModel大小相关的内存以及加载到此ObjectContext中的实体数。

答案 3 :(得分:0)

在使用依赖注入的类中,我遇到了同样的问题,因此using()选项不是替代选择。我的解决方案是将DbContextOptions<Context>添加到构造函数中,并将其作为私有字段添加到类中。然后,您可以致电

_db.Dispose();
_db = new BlockExplorerContext(_dBContextOptions);

在适当的时间。这解决了我内存不足,应用程序被操作系统杀死的问题。