正在优化实体框架查询以进行导入

本文关键字:导入 查询 框架 优化 实体 | 更新日期: 2023-09-27 17:58:01

因为我已经将数据从上下文为MICSYDBCONTEXT的数据库导入到上下文为BRIA_REALDBCONTEXT的数据库。

由于MICSYDBCONTEXT数据库中有280000条记录,我发现只导入10000条记录需要5个小时,因此我迫切需要对查询进行某种优化。我正在使用EntityFramework6,作为一名初级开发人员,我将非常感谢任何帮助。

using (var context = new MICSYDBContext())
{
    var Doc = context.DOCUMENTS.OrderBy(x => x.ID).Take(100);
    try
    {
        using (var context2 = new BRIA_REALDBCONTEXT())
        {
            Stopwatch sw = Stopwatch.StartNew();
            foreach (var item in Doc)
            {
              var DOC = new DLV_DOC();
              DOC.FOLDID = item.ID;
              DOC.FOBJECT = 1;
              DOC.FIO = 40;              
              DOC.FID = newId;
              DOC.FACTIVE = "Y";
              DOC.FCODEWRT = 85;
              DOC.FDOC_COUNT_ORIGINAL = 'N';
              DOC.FUSERINSERT = 85;
              DOC.FMODULE = 1;
              DOC.FSERVICE_TYPE = 1;
              var micsysserviceName = context.DEFDOCTYPE.Where(x => x.ID == item.DOCTYPE).FirstOrDefault();
              if (micsysserviceName != null)
              {
                var service = context2.DLV_SSERVICE.Where(x => x.FNAME == micsysserviceName.NAME && x.FSERVICE_TYPE == 1).FirstOrDefault();
                if (service != null)
                {
                  DOC.FSERVICE = service.FID;
                }
              }
              else
              {
                DOC.FSERVICE = 1;
              }
              var personcorespondent = context2.UCM_PERSON.Where(x => x.FOLD_ID == item.DOCKORID.ToString()).FirstOrDefault();
              if (personcorespondent != null)
              {
                DOC.FPERSON = personcorespondent.FID;
              }
              else
              { 
                  DOC.FPERSON = 142; 
              }
              DOC.FDATEDOC = item.DATA;
              foreach (var docsteps in context.DOCMOVE.Where(x => x.DOCID == item.ID).OrderBy(x => x.DATA))
              {//СТъпка
                DLV_DOC_STEP DLVSTEP = new DLV_DOC_STEP();
                DLVSTEP.FDOC = DOC.FID;
                DLVSTEP.FOBJECT = 1;
                DLVSTEP.FCODEWRT = 85;

                var whofirst = context2.UCM_PERSON.Where(x => x.FOLD_ID == docsteps.WHOFIRST.ToString()).FirstOrDefault();
                if (whofirst != null)
                {
                  DLVSTEP.FPERSON = whofirst.FID;
                }
                var whonext = context2.UCM_PERSON.Where(x => x.FOLD_ID == docsteps.WHONEXT.ToString()).FirstOrDefault();
                if (whonext != null)
                {
                  DLVSTEP.FTOPERSON = whonext.FID;
                }
                context2.DLV_DOC_STEP.Add(DLVSTEP);
              }
              context2.DLV_DOC.Add(DOC);
              i++;
              Console.WriteLine("Added " +i + "DOC");
            }
            context2.SaveChanges();
            Console.WriteLine("End of docs");
            Console.WriteLine(sw.Elapsed.Hours);
          }
        }
        catch (Exception ex)
        {
          string s = ex.Message;
          Console.WriteLine("Problem with record");
          Console.WriteLine(ex.Message);
          Console.WriteLine(ex.StackTrace);
          Console.WriteLine(ex.InnerException);
        }
      }

正在优化实体框架查询以进行导入

这个问题很常见,

我们可以将其分为三类:

  • Add与AddRange性能
  • 阅读&数据库往返
  • 写入&数据库往返

添加与添加范围性能

Add方法将尝试在每次添加新记录时检测更改,而AddRange只做一次。每次检测更改可能需要几分钟时间。

这个问题很容易解决,只需创建两个列表,将实体添加到该列表中,并在列表末尾使用AddRange即可。

var DLV_DOC_STEPS = new List<DLV_DOC_STEP>();
var DLV_DOCS = new List<DLV_DOC>();
foreach(...)
{
    foreach(...)
    {
        DLV_DOC_STEPS.Add(DLVSTEP);
    }
    DLV_DOCS.Add(DOC);
}
context2.DLV_DOC_STEP.AddRange(DLV_DOC_STEPS);
context2.DLV_DOC.AddRange(DLV_DOC);
context2.SaveChanges

另一种解决方案是在启动&amp;在SaveChanges之前重新启用它。我推荐第一种解决方案,但两者都有效。

ctx.Configuration.AutoDetectChangesEnabled = false;
foreach(var item in Doc)
{
    // ...code...
}
ctx.Configuration.AutoDetectChangesEnabled = true;
ctx.SaveChanges();

阅读&数据库往返

应用程序执行太多数据库往返

您多次在以下实体中进行查询:

  • DEFDOCTYPE
  • DLV_servevice
  • UCM_PERSON
  • DOCMOVE

您执行了数百万次数据库往返,这太疯狂了,所以您的应用程序当然非常慢!

解决方案

  • 尝试先加载所有数据,然后使用字典从内存中获取实体(如果表中不包含太多记录)

示例:

var defDoctTypeDict = context.DEFDOCTYPE.AsNoTracking().ToList().ToDictionary(x => x.ID);
foreach(var item in Doc)
{
    DEFDOCTYPE micsysserviceName;
    defDoctTypeDict.TryGetValue(item.DOCTYPE, out micsysserviceName);
    if(micsysserviceName != null)
    {
        // ...code...
    }
    // ...code...
}
  • 尝试通过创建列表并使用"列表"批量加载数据。请改用"包含"你可以在这里看到有人有类似的问题:https://stackoverflow.com/a/38355262/5619143

  • 如果您不需要跟踪实体,请使用AsNoTracking

写入&数据库往返

每次保存记录时,都要执行数据库往返操作。

免责声明:我是项目实体框架扩展的所有者

此库允许执行:

  • BulkSaveChanges
  • 隔板插入件
  • BulkUpdate
  • BulkDelete
  • BulkMerge

您可以在批处理结束时调用BulkSaveChanges,也可以创建一个要插入的列表并直接使用BulkInsert以获得更高的性能。

BulkSaveChanges解决方案(比SaveChanges快得多)

context2.DLV_DOC_STEP.AddRange(DLV_DOC_STEPS);
context2.DLV_DOC.AddRange(DLV_DOC);
context2.BulkSaveChanges

BulkInsert解决方案(比BulkSaveChanges快,但不保存相关实体)

context2.BulkInsert(DLV_DOC_STEPS);
context2.BulkInsert(DLV_DOC);