Performance Entityframework DbContext周围的泛型会导致性能下降吗?

Performance Entityframework DbContext周围的泛型会导致性能下降吗?,performance,entity-framework,generics,dbcontext,Performance,Entity Framework,Generics,Dbcontext,我编写了一个简单的导入/导出应用程序,使用EntityFramework和AutoMapper从源->目标转换数据。它基本上是: 从源表中选择记录的batchSize “映射源->目标实体中的数据” 将新的目标实体添加到目标表中,并保存上下文 我在不到5分钟的时间内移动了大约500k条记录。在我使用泛型重构代码后,性能在5分钟内急剧下降到250条记录 在DbContext上返回DbSet属性的我的委托是否导致了这些问题?还是发生了什么事? 快速非通用代码: 公共类导入器 { 公共

我编写了一个简单的导入/导出应用程序,使用EntityFramework和AutoMapper从源->目标转换数据。它基本上是:

  • 从源表中选择记录的
    batchSize
  • 映射源->目标实体中的数据”
  • 将新的目标实体添加到目标表中,并保存上下文
  • 我在不到5分钟的时间内移动了大约500k条记录。在我使用泛型重构代码后,性能在5分钟内急剧下降到250条记录

    DbContext
    上返回
    DbSet
    属性的我的委托是否导致了这些问题?还是发生了什么事?

    快速非通用代码:
    公共类导入器
    {        
    公共无效导入地址()
    {
    常量int batchSize=50;
    int done=0;
    var src=new SourceDbContext();
    var count=src.Addresses.count();
    while(完成<计数)
    {
    使用(var dest=new DestinationDbContext())
    {
    var list=src.Addresses.OrderBy(x=>x.AddressId).Skip(完成).Take(batchSize.ToList();
    list.ForEach(x=>dest.Address.Add(Mapper.Map(x));
    完成+=批量大小;
    dest.SaveChanges();
    }
    }
    src.Dispose();
    }
    }
    
    (非常)缓慢的通用代码:
    公共类导入器
    其中TSourceContext:DbContext
    其中TDestinationContext:DbContext
    {
    公共无效导入(Func getSourceSet、Func getDestinationSet、Func getOrderBy)
    其中TSourceEntity:class
    where TDestinationEntity:类
    {
    常量int batchSize=50;
    int done=0;
    var ctx=Activator.CreateInstance();
    //此getSourceSet委托是否可能导致问题?
    //加上这个
    var set=getSourceSet(ctx);
    var count=set.count();
    while(完成<计数)
    {
    使用(var dctx=Activator.CreateInstance())
    {
    var list=set.OrderBy(getOrderBy).Skip(done).Take(batchSize.ToList();
    //或者数据库端分页机制是否被getSourceSet委托破坏?
    //加上这个
    var destSet=getdestinationstart(dctx);
    ForEach(x=>destSet.Add(Mapper.Map(x));
    完成+=批量大小;
    dctx.SaveChanges();
    }
    }
    ctx.Dispose();
    }
    }
    
    问题是调用
    Func
    委托您正在做很多事情。将结果值缓存在变量中,这样就可以了。

    问题是调用
    Func
    委托,这是您经常做的事情。将结果值缓存在变量中,这样就可以了。

    我更改了通用代码(请参见//添加此代码),将变量用于
    Func
    结果,但速度仍然很慢…它仍然在
    while
    循环中。正确,由于缓存的原因,它需要在每个批处理后创建新的上下文。。否则对象会变得很大。您可以使用
    Detach
    方法从上下文中删除处理过的对象。我更改了通用代码(请参见//添加此代码)以使用
    Func
    结果的变量,但它仍然很慢…它仍然在
    循环中。正确,由于缓存的原因,它需要在每个批处理之后创建一个新的上下文。。否则对象会变得很大。您可以使用
    Detach
    方法从上下文中删除处理过的对象。为什么要在while块的每次迭代中创建新的目标实例?您可以使用while语句之外的内容移动该应用程序。请配置应用程序并确定!如果手头没有分析器,请暂停调试器10次。您可以在注释中提出问题,这些问题可以通过时间跟踪(秒表)轻松验证。为什么要在while块的每次迭代中创建新的目标实例?您可以使用while语句之外的内容移动该应用程序。请配置应用程序并确定!如果您手头没有分析器,请暂停调试器10次。您可以在注释中提出问题,这些问题可以通过时间跟踪(秒表)轻松验证。
    public class Importer
    {        
        public void ImportAddress()
        {
            const int batchSize = 50;
            int done = 0;
            var src = new SourceDbContext();
    
            var count = src.Addresses.Count();
    
            while (done < count)
            {
                using (var dest = new DestinationDbContext())
                {
                    var list = src.Addresses.OrderBy(x => x.AddressId).Skip(done).Take(batchSize).ToList();
                    list.ForEach(x => dest.Address.Add(Mapper.Map<Addresses, Address>(x)));
    
                    done += batchSize;
    
                    dest.SaveChanges();
                }
            }
    
            src.Dispose();
        }
    }
    
    public class Importer<TSourceContext, TDestinationContext>
        where TSourceContext : DbContext
        where TDestinationContext : DbContext
    {
        public void Import<TSourceEntity, TSourceOrder, TDestinationEntity>(Func<TSourceContext, DbSet<TSourceEntity>> getSourceSet, Func<TDestinationContext, DbSet<TDestinationEntity>> getDestinationSet, Func<TSourceEntity, TSourceOrder> getOrderBy) 
            where TSourceEntity : class
            where TDestinationEntity : class
        {
            const int batchSize = 50;
            int done = 0;
            var ctx = Activator.CreateInstance<TSourceContext>();
            //Does this getSourceSet delegate cause problems perhaps?
    
            //Added this
            var set = getSourceSet(ctx);
    
            var count = set.Count(); 
    
            while (done < count)
            {
                using (var dctx = Activator.CreateInstance<TDestinationContext>())
                {
                    var list = set.OrderBy(getOrderBy).Skip(done).Take(batchSize).ToList(); 
                    //Or is the db-side paging mechanism broken by the getSourceSet delegate?
                    //Added this
                    var destSet = getDestinationSet(dctx);
                    list.ForEach(x => destSet.Add(Mapper.Map<TSourceEntity, TDestinationEntity>(x)));
    
                    done += batchSize;
                    dctx.SaveChanges();
                }
            }
    
            ctx.Dispose();
        }
    }