Asp.net EF缓存:如何在将对象插入HttpRuntime缓存之前完全分离对象? 一些背景:

Asp.net EF缓存:如何在将对象插入HttpRuntime缓存之前完全分离对象? 一些背景:,asp.net,entity-framework,caching,memory-leaks,entity-framework-5,Asp.net,Entity Framework,Caching,Memory Leaks,Entity Framework 5,使用: .NET4.5(如果没有痛苦,考虑迁移到4.5.1) 网络表单 实体框架5,启用延迟加载 每个请求的上下文 IIS 8 Windows 2012数据中心 关注点:内存使用 在我们目前正在进行的项目中,可能是我们的第一个更大的项目,我们经常读取来自CSV导入的更大数据块,这些数据可能会在很长一段时间内保持不变 除非有人明确地重新导入CSV数据,否则它们保证是相同的,这在我们项目中的多个地方都会发生,并且对于用户经常阅读的一些常规文档,也会使用类似的方法。我们已决定将此数据缓存在Http

使用:

  • .NET4.5(如果没有痛苦,考虑迁移到4.5.1)
  • 网络表单
  • 实体框架5,启用延迟加载
  • 每个请求的上下文
  • IIS 8
  • Windows 2012数据中心
关注点:内存使用

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

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

它是这样的,我们提取了大约15000条主要由字符串组成的记录

//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;
    }
}
//myObject和相关方法是占位符
公共静态列表GetMyCachedObjects()
{
if(CacheManager.Exists(KeyConstants.keyConstantForMyObject))
{
以列表形式返回CacheManager.Get(KeyConstants.keyConstantForMyObject);
}
其他的
{
List myObjectList=framework.objectProvider.GetMyObjects();
CacheManager.Add(KeyConstants.keyConstantForMyObject,MyObject列表,true,5000);
返回myObjectList;
}
}
上述方法的数据检索非常简单,如下所示:

public List<myObject> GetMyObjects()
{
    return context.myObjectsTable.AsNoTracking().ToList();
}
public List GetMyObjects()
{
返回context.myObjectsTable.AsNoTracking().ToList();
}
关于代码结构可能有一些事情要说,但这不是我目前关心的

当我发现内存使用率很高,并且发现许多代码可以优化的地方时,我就开始分析我们的项目。我以前从未同时面对过300个用户,我们自己做的内部测试不足以显示内存问题。我强调并修复了大量内存泄漏,但我想了解一些与实体框架相关的未知数

在上面的例子中,使用ANTS Profiler,我注意到“myObject”和其他类似的对象引用了许多System.Data.Entity.DynamicProxies.myObject,另外还有许多EntityKey,它们保存着整数。他们拿的不多,但数量相对较高

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

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

我的进度: 我做了一些研究,发现我可能正在缓存与这些对象相关的实体框架。我用NoTracking提取了它们,但记忆中仍然有那些动态的线索,它们可能也会保留其他东西

重要提示:我观察到ObjectContext(74)的一些活动实例正在缓慢增长,但没有持有dbContext的unitOfWork实例。这些似乎是根据每个请求正确处理的

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

问题:
  • 基本上,当涉及实体框架时,我的缓存方法有什么错
  • 内存中越来越多的对象上下文是一个问题吗?我知道缓存最终会过期,但我担心打开的连接或此上下文可能包含的任何其他内容
  • 我应该在将内容插入缓存之前将其与上下文分离吗
  • 如果是,最好的方法是什么。尤其是对于List,除了遍历集合并逐个调用detach之外,我想不出其他任何事情
  • 附加问题:大约40%的消耗内存是空闲的(未分配的),我不知道.NET为什么要提前保留这么多空闲内存

您可以尝试使用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();
   );
}
公共类MyObject 2{
公共int ID{get;set;}
公共字符串名称{get;set;}
}
公共列表GetObjects(){
返回framework.provider.GetObjects()。选择(
x=>新的MyObject 2{
ID=x.ID,
Name=x.Name
}).ToList();
);
}
因为您将存储普通的c#对象,所以不必担心动态代理。您根本不必调用detach。此外,您只能存储少数属性


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

此处减少内存的步骤:

  • 重新
    new
    上下文,通常 不要尝试从上下文中删除内容。或者将其设置为“分离”。 它就像放在电话盒里的屁一样到处乱放
    例如
    context=newmycontext.

    但如果可能的话,你应该
使用(var context=newmycontext){….}
//短期上下文是最佳实践

  • 根据您的上下文,您可以设置配置
this.Configuration.LazyLoadingEnabled=false;

this.Configuration.ProxyCreationEnabled=false// 我会重新设计一下解决方案:

  • 您将所有数据作为一个条目存储在缓存中
我将移动此项,并为每个缓存项创建一个条目<
 this.Configuration.LazyLoadingEnabled = false;  
 this.Configuration.ProxyCreationEnabled = false;   //<<<<<<<<<<< THIS one   
 this.Configuration.AutoDetectChangesEnabled = false;