EF4 内存/效能改善一案

本文所要分享的内容在特定的背景下,请予以注意。

补充:有朋友回复不明本文在分享什么,这里给予补充说明
大致的情况是这样的,有数百个相同架构的 DB 分配给不同的客户使用。然而他们共享一套高层的逻辑组件,这些组件需要在某些情况下操作所有的这些 DB (如提取某些资料后发送邮件等)。这样导致了链接发生变化,实例化 ObjectContext 并及时的释放分配的资源,但 释放分配资源 这个动作并没有达到预期的效果,内存一直被吃着,效率一直下降着。文末的代码有例子。为了解决这个问题,搜索了不少网上的资料(都是在提醒 using 和 Detach 或是 ToList),但这不是合适的解决方案。在经过一番排查后发现 EF6 不存在这个问题,效率很高,内存也不会持续增长,查看代码得知 EF4 随着链接的不同会实例化多个 MetadataWorkspace 而不去思考 Metadata 的相同性(EF6 不会)。最后文末给出了一个 BuildFromCache 的方法来解决这个问题。

背景:使用 EF4 框架,操纵多个拥有同样架构的 DB

问题:发现随着 DB 数量的增加其内存几乎耗尽,查询效率极其低下。

过程:

  1. 接到这个问题开始便大量 Google 关键词: EF Memory Leaks | EF Dispose 等,发现有不少类似的问题但都木有一个可行的答案,也没有解决这个问题。而后决定使用工具分析。
  2. 使用  ANTS Memory Profiler 7(福利进行内存分析,知道了哪些对象在吃着内存。
  3. Google 了一番那些吃内存对象的关键词,也有相当多的篇幅在咨询类似的问题,如: MetadataWorkspace | EntityConnection等。
  4. 尝试使用了一下 EF6,这个问题居然不存在了-_- !,尼玛看代码吧。
  5. EntityConnection中提供的 GetMetadataWorkspace 映入眼帘,就是它了,因为 EF4 它爱上了 new

解法:EntityConnection 实例化的时候始终共享工作区(下面的范例代码中的 BuildFromCache(string catalog) 方法)。

重现:

  • 制造背景:
View Code
DECLARE @num INT 
DECLARE @max INT 
DECLARE @catalog NVARCHAR(16)
DECLARE @dbFullName NVARCHAR(200)
DECLARE @logFullName NVARCHAR(200)
SET @num = 0
SET @max = 1 
WHILE @num < @max 
    BEGIN
        SET @catalog = 'DB' + REPLACE(STR(@num, 3), SPACE(1), '0')
        
        SET @dbFullName = N'D:\Database\' + @catalog + N'.mdf'
        SET @logFullName = N'D:\Database\' + @catalog + N'.LDF'
        
        RESTORE DATABASE @catalog
        FROM  DISK = N'D:\Database\Backup\AdventureWorks2008R2.bak' 
        WITH  FILE = 1,  
        MOVE N'AdventureWorks2008R2_Data' TO @dbFullName,  
        MOVE N'AdventureWorks2008R2_Log' TO @logFullName,  
        NOUNLOAD,  
        STATS = 10
        
        SET @num = @num + 1 
    END
  • 测试代码:
View Code
using System;
using System.Collections.Generic;
using System.Data.EntityClient;
using System.Data.Metadata.Edm;
using System.Data.SqlClient;
using System.Diagnostics;
using System.Globalization;
using System.Linq;

namespace Foo
{
    internal class Program
    {
        private static void Main(string[] args)
        {
            Stopwatch watch = Stopwatch.StartNew();
            IEnumerable<string> catalogs = GenerateCatalogs();
            foreach (string catalog in catalogs)
            {
                //using (AdventureWorksModelContainer container = ContainerBuilder.Build(catalog))
                using (AdventureWorksModelContainer container = ContainerBuilder.BuildFromCache(catalog))
                {
                    Employee employee = container.Employee.First(o => true);
                }
            }
            watch.Stop();
            Console.WriteLine(watch.Elapsed);
            Console.ReadKey();
        }

        private static IEnumerable<string> GenerateCatalogs()
        {
            var names = new string[20];
            for (int i = 1; i <= names.Length; i++)
                yield return string.Concat("DB", i.ToString(CultureInfo.InvariantCulture).PadLeft(3, '0'));
        }
    }

    internal class ContainerBuilder
    {
        private const string Metadata = "res://*/AdventureWorksModel.csdl|res://*/AdventureWorksModel.ssdl|res://*/AdventureWorksModel.msl";
        private const string Connection = "data source=.;initial catalog={0};integrated security=True;MultipleActiveResultSets=True";

        private static MetadataWorkspace _metadataWorkspace;

        public static AdventureWorksModelContainer Build(string catalog)
        {
            string esc = new EntityConnectionStringBuilder
                {
                    Provider = "System.Data.SqlClient",
                    ProviderConnectionString = string.Format(Connection, catalog),
                    Metadata = Metadata
                }.ToString();
            return new AdventureWorksModelContainer(esc);
        }

        public static AdventureWorksModelContainer BuildFromCache(string catalog)
        {
            string connection = string.Format(Connection, catalog);
            if (_metadataWorkspace == null)
            {
                string esc = new EntityConnectionStringBuilder
                    {
                        Provider = "System.Data.SqlClient",
                        ProviderConnectionString = connection,
                        Metadata = Metadata
                    }.ToString();
                using (var ec = new EntityConnection(esc))
                {
                    _metadataWorkspace = ec.GetMetadataWorkspace();
                }
            }
            return new AdventureWorksModelContainer(new EntityConnection(_metadataWorkspace, new SqlConnection(connection)));
        }
    }
}

 

转载于:https://www.cnblogs.com/le618n/archive/2013/04/24/3041410.html

ADO.NET Entity Framework (EF) 提供了一种称为对象上下文的机制,可用于跟踪对象的状态。对象上下文是一个包含在 EF 中的对象,它可以跟踪已加载到内存中的实体对象的状态,并将更改保存回数据库。 在对象上下文中,每个实体都有一个状态,可以是 Added、Unchanged、Modified 或 Deleted。当你向对象上下文中添加一个新实体时,该实体的状态将被设置为 Added。当你从数据库中检索一个实体并将其添加到对象上下文中时,该实体的状态将被设置为 Unchanged。在对实体进行更改后,其状态会变为 Modified。当你从对象上下文中删除实体时,其状态将被设置为 Deleted。 使用对象上下文,你可以轻松跟踪实体的状态,并在需要时将更改保存回数据库。你可以使用以下代码创建一个新的对象上下文: ``` using (var context = new MyDbContext()) { // Perform database operations here } ``` 在这个示例中,MyDbContext 是你创建的 DbContext 类型的实例。在 using 语句块中,你可以执行所有的数据库操作,包括查询、插入、更新和删除。当 using 语句块结束时,对象上下文将被自动释放。 如果你想手动更改实体的状态,可以使用以下代码: ``` context.Entry(entity).State = EntityState.Added; // Added, Modified, or Deleted ``` 在这个示例中,entity 是你想要更改状态的实体对象。你可以将其状态设置为 Added、Modified 或 Deleted,具体取决于你想要执行的操作。 希望这可以帮助回答你的问题。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值