我有一个EF 4.2 EDMX模型,我在多租户应用程序中使用它。我连接到大约100个使用相同EDM模型的数据库。第一次访问每个数据库时,我的工作集上升了大约12Mb,这似乎主要由EDM元数据缓存占用。内存使用率永远不会下降。我认为元数据/查询缓存可以共享,因为它是相同的模型。
寻找减少内存占用的建议,但我怀疑我无法控制这一点。
注意:同样的场景不是CodeFirst的问题(我们也在使用它),但是我们有很多代码仍然使用EDMX模型,现在无法将其转换。
谢谢!
答案 0 :(得分:5)
我相信你可以通过自己缓存MetadataWorkspace来获得你想要的东西。这基本上是DbContext在使用Code First时在内部执行的操作。这并不容易,但我制定了一个我认为应该有效的快速原型。
这里的基本思想是让EF创建一次MetadataWorkspace,然后缓存它并在每次需要创建上下文实例时显式使用它。这显然仅在每个上下文实例使用相同模型时才有效 - 即。相同的EDMX。为了完成这项工作,我创建了一个处理缓存的派生ObjectContext:
public class SingleModelCachingObjectContext : ObjectContext
{
private static readonly object WorkspaceLock = new object();
private static MetadataWorkspace _workspace;
public SingleModelCachingObjectContext(string connectionStringName)
: base(CreateEntityConnection(connectionStringName))
{
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
((EntityConnection)Connection).StoreConnection.Dispose();
}
}
private static EntityConnection CreateEntityConnection(string connectionStringName)
{
lock (WorkspaceLock)
{
if (_workspace == null)
{
_workspace = new EntityConnection("name=" + connectionStringName).GetMetadataWorkspace();
}
}
var builder =
new DbConnectionStringBuilder
{
ConnectionString = ConfigurationManager.ConnectionStrings[connectionStringName].ConnectionString
};
var storeConnection = DbProviderFactories.GetFactory((string)builder["provider"]).CreateConnection();
storeConnection.ConnectionString = (string)builder["provider connection string"];
return new EntityConnection(_workspace, storeConnection);
}
}
然后,您可以通过在DbContext类上创建构造函数来使用它,如下所示:
public MyDbContext(string connectionStringName)
: base(new SingleModelCachingObjectContext(connectionStringName),
dbContextOwnsObjectContext: true)
{
}
这是它的工作原理。当您创建DbContext的实例时,它会依次创建一个SingleModelCachingObjectContext实例,该实例将传递您要使用的EF连接字符串的名称。它还告诉DbContext在处理DbContext时处理这个ObjectContext。
在SingleModelCachingObjectContext中,EF连接字符串用于创建MetadataWorkspace,并在创建后将其缓存在静态字段中。这是一个非常简单的缓存和带锁的简单线程安全 - 随意使它更适合您的应用程序需求。
获取MetadataWorkspace后,现在将解析EF连接字符串以获取存储连接字符串和提供程序。然后,这将用于创建正常的商店连接。
商店连接和缓存的MetadataWorkspace用于创建EntityConnection,然后创建将使用缓存的MetadataWorkspace而不是使用常规缓存机制的ObjectContext。
此ObjectContext用于支持DbContext。重写Dispose方法,以便存储连接不会泄漏。当处理DbContext时,它将处理ObjectContext,而ObjectContext又将调用Dispose,并且将处理商店连接。
除了确保它运行之外,我还没有真正测试过这个。知道它是否真的有助于你的内存使用问题,这将是非常有趣的。
答案 1 :(得分:0)
我对你的问题没有多少经验,但我的头脑中有一些建议:
您可以使用ClearCache()
方法吗?
也可以尝试使用:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Remove<IncludeMetadataConvention>();
}
请注意,如果删除EdmMetadata表,则无法检查数据库架构是否与模型匹配。
尝试使用T4模板预生成视图,如DbContext模板或Poco生成模板(http://blogs.msdn.com/b/adonet/archive/2010/01/25/walkthrough-poco-template-for-the -entity-framework.aspx)。
问候。
答案 2 :(得分:0)
我正在补充Arthur给出的答案。当我使用Arthur提出的解决方案时,我遇到了一个问题。我有几个存储过程映射到EF模型,当我执行它们时,它们失败了以下消息。
发现了System.InvalidOperationException Message =“EntityCommand.CommandText的值对StoredProcedure命令无效.EntityCommand.CommandText值的格式必须为'ContainerName.FunctionImportName'。” 源= “System.Data.Entity的”
发生这种情况是因为在使用MetadataWorkspace初始化上下文时未设置DefaultContainerName。为了正常工作,我在下面进行了更改。
我使用稍微不同的方法来使用EFConnection而不是读取表单配置,因为在多租户DB的情况下,我们不会在配置中输入连接字符串并在运行时传递它。还使用了泛型,以便您可以在不同的上下文之间共享实现。还要更改上面的实现,以便仅在需要时(即设置工作空间时)锁定线程。
public class SingleModelCachingObjectContext<T> : ObjectContext
{
private static readonly object WorkspaceLock = new object();
private static MetadataWorkspace _workspace;
public SingleModelCachingObjectContext(string connectionString)
: base(CreateEntityConnection(connectionString))
{
DefaultContainerName = typeof (T).Name;
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
((EntityConnection)Connection).StoreConnection.Dispose();
}
}
protected static EntityConnection CreateEntityConnection(string connectionString)
{
if (_workspace == null)
{
lock (WorkspaceLock)
{
_workspace = new EntityConnection(connectionString).GetMetadataWorkspace();
}
}
var builder =
new DbConnectionStringBuilder
{
ConnectionString = connectionString
};
var storeConnection = DbProviderFactories.GetFactory((string)builder["provider"]).CreateConnection();
storeConnection.ConnectionString = (string)builder["provider connection string"];
return new EntityConnection(_workspace, storeConnection);
}
}