【问题标题】:Entity framework uses a lot of memory实体框架使用大量内存
【发布时间】:2011-05-15 18:11:24
【问题描述】:

这是来自 ANTS 内存分析器的图像。它看到有很多对象保存在内存中。我怎样才能发现我做错了什么?

**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
还有初始化UnitOfWork的UnitOfWorkFactory:

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

为了使用这个,在应用程序启动中我使用了结构映射:

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

【问题讨论】:

  • 您的查询是什么样的? SELECT * FROM LargeTable ?
  • 此外,您的 EF 查询是什么样的?当您只需要几个实体时,您是否不小心将许多实体拉入内存?
  • 您必须描述您正在使用 EF 做什么、正在构建什么类型的应用程序以及如何使用实体。否则这个问题可能会因为我们无法回答而被关闭,因为我们无法回答它。
  • @Ladislav Mrnka:我正在考虑发布什么。我有很多代码。我相信选择查询不会说明太多,我会发布一些东西。
  • @Ladislav Mrnka、@Henk Holterman、@Julie Lerman:我添加了更多信息。

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


【解决方案1】:

我有一种预感,你不会处理上下文。
我建议您在与数据库交互后处理上下文。

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

[编辑]

据我所知,您缓存而不处置您的EFUnitOfWork 对象。它是一次性的,这是正确的,但我看不到何时调用一次性。似乎您在所有应用程序运行时都持有对上下文的引用。
此外,您为每个线程创建并持有一个上下文,这会使情况变得更糟。

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

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

如果我是你,我会:

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

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

【讨论】:

  • 虽然这对于 Web 应用程序和服务操作(以及其他特定架构)来说是极好的建议,但我不一定建议对每个场景(使用和处置)都这样做。这取决于应用程序正在做什么以及造成问题的原因。在某些客户端应用程序场景中,保留上下文很有帮助(同时注意内存使用和 EF 缓存的滥用)。 OTOH,如果这是一个 Web 应用程序或服务,只要包含的类实例超出范围,.NET 就会抛出上下文。
  • @Julie - 我同意在某些情况下最好保留上下文。但是,我认为在大多数情况下,它更健壮、可扩展并且更不容易尽快处理掉错误。
  • @Naor - 从您的代码中不清楚 EFUnitOfWork 是如何实例化的。
  • @Alex Aza:你说得对 - 我用创建 UnitOfWork 的工厂更新了代码。
  • @Naor - 在我的回答中添加了一些 cmets。
【解决方案2】:

我想到了几件事:

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

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

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

这能回答你的问题吗?

【讨论】:

  • @Omar AL Zabir:你怎么知道我在使用 N-Tire 架构?您推荐哪种架构?
  • 查看您的代码,是的,没有 dispose。 UnitOfWork 在当前线程上创建了一个 Objectcontext,但随后并没有 Dispose 它。
  • 我通常在我的 Web 应用程序中执行此操作:在 Application_EndRequest 上,我遍历 HttpContext.Items 中的所有项目,查看哪些是 IDisposable 并调用它们的 Dispose 方法。这将清除所有 UnitOfWork。但更多的架构问题是,UnitOfWork 不应该存储在 Context 中。您可以让 UnitOfWork 起源于任何地方。它可以起源于业务层,可能在某些业务外观方法中。 UnitOfwork 用于 using(...) 块中,以清楚地标记工作的开始位置和工作结束的位置。
  • @Omar AL Zabir:UnitOfWork 不应存储在 Context 中的原因是什么?
  • UnitOfWork 在设计上是指在某个模块内完成的一小部分工作。 UnitOfWork 的一个实例只完成一项工作。当您将其存储在 Context 中时,您实际上是在重用 UnitOfWork 的一个实例来完成多项工作。这意味着 UnitOfWork 成为一个有状态的对象,它被用来做非单元工作。
【解决方案3】:

您是否偶尔清除ObjectContext。如果您长时间保持 ObjectContext 活动,这将消耗与 EntityDataModel 的大小和加载到此 ObjectContext 的实体数量相关的内存。

【讨论】:

  • @Naor:正如 Alex Aza 所假设的那样。我没有看到对 objectcontext / EFUnitOfwork 的任何处置。 @Alex Aza:我同意你的观点,设计看起来相当复杂。
【解决方案4】:

我在一个使用依赖注入的类中遇到了同样的问题,所以using() 选项不是替代方案。我的解决方案是将DbContextOptions&lt;Context&gt; 添加到构造函数并作为类的私有字段。然后,您可以调用

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

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-10-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多