正在优化实体框架查询以进行导入
本文关键字:导入 查询 框架 优化 实体 | 更新日期: 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
另一种解决方案是在启动&;在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);