实体框架内存使用与共享模型

jle*_*lew 1 memory entity-framework metadata edmx

我有一个EF 4.2 EDMX模型,我在多租户应用程序中使用它.我连接到大约100个使用相同EDM模型的数据库.第一次访问每个数据库时,我的工作集上升了大约12Mb,这似乎主要由EDM元数据缓存占用.内存使用率永远不会下降.我认为元数据/查询缓存可以共享,因为它是相同的模型.

寻找减少我的内存占用的建议,但我怀疑我无法控制它.

注意:同样的场景不是CodeFirst的问题(我们也在使用它),但我们有很多代码仍然使用EDMX模型,现在无法将其转换.

谢谢!

Art*_*ers 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);
    }
}
Run Code Online (Sandbox Code Playgroud)

然后,您可以通过在DbContext类上创建构造函数来使用它,如下所示:

public MyDbContext(string connectionStringName)
    : base(new SingleModelCachingObjectContext(connectionStringName),
           dbContextOwnsObjectContext: true)
{
}
Run Code Online (Sandbox Code Playgroud)

这是它的工作原理.当您创建DbContext的实例时,它会依次创建一个SingleModelCachingObjectContext实例,该实例将传递您要使用的EF连接字符串的名称.它还告诉DbContext在处理DbContext时处理这个ObjectContext.

在SingleModelCachingObjectContext中,EF连接字符串用于创建MetadataWorkspace,并在创建后将其缓存在静态字段中.这是一个非常简单的缓存和简单的线程安全锁定 - 随意使它更适合您的应用程序的需求.

获得MetadataWorkspace后,现在将解析EF连接字符串以获取存储连接字符串和提供程序.然后,这用于创建正常的商店连接.

存储连接和缓存的MetadataWorkspace用于创建EntityConnection,然后创建将使用缓存的MetadataWorkspace而不是使用常规缓存机制的ObjectContext.

此ObjectContext用于支持DbContext.重写Dispose方法,以便存储连接不会泄漏.当处理DbContext时,它将处理ObjectContext,它将依次调用Dispose并将处理存储连接.

除了确保它运行之外,我还没有真正测试过这个.知道它是否真的有助于你的内存使用问题,这将是非常有趣的.