一些背景:
合作:
- .NET 4.5(如果轻松的话考虑迁移到 4.5.1)
- 网络表单
- Entity Framework 5,启用延迟加载
- 每个请求的上下文
- IIS 8
- Windows 2012 数据中心
关注点:内存使用
在我们目前正在进行的项目(可能是我们的第一个更大的项目)中,我们经常读取来自 CSV 导入的一些更大的数据 block ,这些数据可能会在很长一段时间内保持不变。
除非有人明确地重新导入 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,此外还有许多保存整数的EntityKey。他们拿的不多,但数量相对较多。
例如,“myObject”的 124 个实例正在引用近 300 个 System.Data.Entity.DynamicProxies。
通常它看起来像这样,无论对象是什么: 一些缓存条目,一些我缓存的对象,我现在注意到它们中的许多已经与之前的缓存、动态代理和 objectContext 分离。我不知道如何解开它们。
我的进度:
我做了一些研究,发现我可能正在缓存与这些对象相关的 Entity Framework 的内容。我已经用 NoTracking 拉取了它们,但内存中仍然有那些 DynamicProxies,它们可能还保留着其他东西。
重要提示:我观察到 ObjectContext (74) 的一些实时实例正在缓慢增长,但没有持有 dbContext 的 unitOfWork 实例。这些似乎根据请求得到了正确的处理。
我知道如何从 dbContext 中分离、附加或修改条目的状态,该条目包含在工作单元中,而且我经常这样做。然而,这似乎还不够,或者我要求的是不可能的。
问题:
- 基本上,当涉及到 Entity Framework 时,我的缓存方法做错了什么?
- 内存中不断增长的对象上下文数量是一个问题吗?我知道缓存最终会过期,但我担心打开的连接或该上下文可能保存的任何其他内容。
- 在将所有内容插入缓存之前,我是否应该将其从上下文中分离出来?
- 如果是,最好的方法是什么。特别是对于 List,除了迭代集合并一一调用分离之外,我想不出其他任何东西。
- 额外问题:大约 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();
);
}
由于您将存储普通的 C# 对象,因此您不必担心动态代理。您根本不必对任何事情调用 detach 。此外,您只能存储少量属性。
即使您禁用跟踪,您也会看到动态代理,因为 EF 使用从您的类派生的动态类,该类存储实体的额外元数据信息(例如外键名称等与其他实体的关系)。
关于asp.net - EF缓存: How to detach objects *completely* before inserting them into HttpRuntime cache?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20483073/