EF缓存:如何在将对象插入HttpRuntime缓存之前完全分离对象*?

时间:2013-12-09 23:32:10

标签: asp.net entity-framework caching memory-leaks entity-framework-5

一些背景:

使用:

  • .NET 4.5(考虑迁移到4.5.1,如果它是无痛的
  • 网络表单
  • 实体框架5,启用了延迟加载
  • 每个请求的上下文
  • IIS 8
  • Windows 2012 Datacenter

关注点:内存使用

在我们目前正在进行的项目中,可能是我们的第一个更大的项目,我们经常阅读来自CSV导入的更大块数据,这些数据可能会在很长一段时间内保持不变。

除非有人明确地重新导入CSV数据,否则它们保证是相同的,这发生在我们项目的多个地方,类似的方法用于一些常常由用户阅读的常规文档。我们决定将这些数据缓存在HttpRuntime缓存中。

它是这样的,我们拉大约15,000条记录,主要由字符串组成。

//myObject and related methods are placeholders
public static List<myObject> GetMyCachedObjects()
{
    if (CacheManager.Exists(KeyConstants.keyConstantForMyObject))
    {
        return CacheManager.Get(KeyConstants.keyConstantForMyObject) as List<myObject>;
    }
    else
    {
        List<myObject> myObjectList = framework.objectProvider.GetMyObjects();
        CacheManager.Add(KeyConstants.keyConstantForMyObject, myObjectList, true, 5000);
        return myObjectList;
    }
}

上述方法的数据检索非常简单,如下所示:

public List<myObject> GetMyObjects()
{
    return context.myObjectsTable.AsNoTracking().ToList();
}

关于代码结构可能有些事要说,但目前我并不关心这一点。

我一看到内存使用率很高就开始分析我们的项目,并发现许多部分可以优化我们的代码。我以前从未面对300个同时用户,我们自己完成的内部测试不足以显示内存问题。我已经突出显示并修复了许多内存泄漏,但我想了解一些与Entity Framework相关的未知数。

鉴于以上示例,并使用ANTS Profiler,我注意到' myObject '和其他类似对象引用了许多 System.Data.Entity.DynamicProxies .myObject,还有很多 EntityKeys ,它们保留整数。他们没有太多,但他们的数量相对较高。

例如,124个“ myObject ”实例引用了近300个System.Data.Entity.DynamicProxies。

通常它看起来像这样,无论对象是什么: 一些缓存条目,我缓存的一些对象,我现在注意到它们中的许多已经从dbContext事先缓存,动态代理和objectContext中分离出来。我不知道如何解开它们。

enter image description here

我的进步:

我做了一些研究,发现我可能正在缓存与这些对象相关的实体框架。我用 NoTracking 来提取它们但是内存中仍然存在那些可能还有其他东西的DynamicProxies。

重要:我观察到一些ObjectContext(74)的实时实例,慢慢增长,但没有我的unitOfWork实例持有dbContext。这些似乎可以根据要求妥善处理。

我知道如何从我的dbContext中分离,附加或修改条目的状态,dbContext包含在unitOfWork中,我经常这样做。然而,这似乎不够,或者我要求不可能。

问题:

  • 基本上,在实体框架方面我的缓存方法有什么问题?
  • 内存中对象上下文的数量越来越多,我知道缓存最终会过期,但我担心开放连接或此上下文可能持有的任何其他内容。
  • 在将其插入缓存之前,我是否应该从上下文中删除所有内容?
  • 如果是,那么最好的方法是什么。特别是对于List,我不能想到任何其他事情,只能迭代集合并逐个调用detach。
  • 奖金问题:大约40%的消耗内存是免费的(未分配),我不知道为什么.NET提前预留了这么多可用内存。

3 个答案:

答案 0 :(得分:4)

您可以尝试使用具有SELECT方法的特定属性的非实体类。

public class MyObject2 {
   public int ID { get; set; }  
   public string Name { get; set; }
}

public List<MyObject2> GetObjects(){  
   return framework.provider.GetObjects().Select(
      x=> new MyObject2{
         ID = x.ID ,
         Name = x.Name
      }).ToList();
   );
}

由于您将存储普通的c#对象,因此您不必担心动态代理。你根本不需要调用任何东西。此外,您只能存储少数属性。

即使你禁用跟踪,你也会看到动态代理,因为EF使用从你的类派生的动态类,它为实体存储额外的元数据信息(关系,例如外键的名称等等)。

答案 1 :(得分:1)

这里减少内存的步骤:

  • 经常重新new上下文  不要尝试从上下文中删除内容。或者将其设置为分离。  它像电话盒里的屁一样挂着  例如context = new MyContext.
    但如果可能的话,你应该
  

使用(var context = new Mycontext){....}
  //短暂的上下文是最佳实践

  • 使用您的上下文,您可以设置配置
 this.Configuration.LazyLoadingEnabled = false;  
 this.Configuration.ProxyCreationEnabled = false;   //<<<<<<<<<<< THIS one   
 this.Configuration.AutoDetectChangesEnabled = false;

如果您仍然觉得他们正在占用内存,您可以禁用代理。 但如果您首先将using应用于上下文,则可能会出现这种情况。

答案 2 :(得分:0)

我会稍微重新设计一下解决方案:

  • 您将所有数据存储为缓存中的单个条目

我会移动它,每个缓存项都有一个条目。

  • 您正在使用HTTPRuntime缓存

我会使用Appfabric Caching,也是MS,也是免费的。

  • 不确定从
  • 调用该代码的位置

我会在应用程序启动时调用它,然后当用户需要时所有数据都在内存中

  • 您正在使用Entity SQL

为此,我将使用实体数据阅读器http://msdn.microsoft.com/en-us/library/system.data.entityclient.entitydatareader(v=vs.110).aspx

另见:

http://msdn.microsoft.com/en-us/data/hh949853.aspx