[英]Very slow runtime with Entity Framework nested loop (using nav properties)
现在,我正在尝试为使用非常标准化的模式的调查提交程序编写一种方法。
我有一种方法旨在为一组人员生成调查,在此过程中将几个不同的EF模型链接在一起。 但是,对于最小的团队规模,此方法的运行速度极慢(对于4人团队,这需要11.2秒才能执行,而对于8人团队,则要花费103.9秒)。 经过分析,我发现以下代码块占用了75%的运行时:
var TeamMembers = db.TeamMembers.Where(m => m.TeamID == TeamID && m.OnTeam).ToList();
foreach (TeamMember TeamMember in TeamMembers)
{
Employee employee = db.Employees.Find(TeamMember.EmployeeID);
SurveyForm form = new SurveyForm();
form.Submitter = employee;
form.State = "Not Submitted";
form.SurveyGroupID = surveygroup.SurveyGroupID;
db.SurveyForms.Add(form);
db.SaveChanges();
foreach (TeamMember peer in TeamMembers)
{
foreach (SurveySectionDetail SectionDetail in sectionDetails)
{
foreach (SurveyAttributeDetail AttributeDetail in attributeDetails.Where(a => a.SectionDetail.SurveySectionDetailID == SectionDetail.SurveySectionDetailID) )
{
SurveyAnswer answer = new SurveyAnswer();
answer.Reviewee = peer;
answer.SurveyFormID = form.SurveyFormID;
answer.Detail = AttributeDetail;
answer.SectionDetail = SectionDetail;
db.SurveyAnswers.Add(answer);
db.SaveChanges();
}
}
}
}
对于如何缩减运行时间,我确实感到茫然。 这仅仅是我为拥有这么多相关实体而付出的代价吗? 我知道联接是昂贵的操作,并且我基本上拥有3个?还是我忽略了一些效率低下的问题?
谢谢你的帮助!
编辑:根据Xiaoy312的要求,这是sectionDetails和attributeDetails的定义方式:
SurveyTemplate template = db.SurveyTemplates.Find(SurveyTemplateID);
List<SurveySectionDetail> sectionDetails = new List<SurveySectionDetail>();
List<SurveyAttributeDetail> attributeDetails = new List<SurveyAttributeDetail>();
foreach (SurveyTemplateSection section in template.SurveyTemplateSections)
{
SurveySectionDetail SectionDetail = new SurveySectionDetail();
SectionDetail.SectionName = section.SectionName;
SectionDetail.SectionOrder = section.SectionOrder;
SectionDetail.Description = section.Description;
SectionDetail.SurveyGroupID = surveygroup.SurveyGroupID;
db.SurveySectionDetails.Add(SectionDetail);
sectionDetails.Add(SectionDetail);
db.SaveChanges();
foreach (SurveyTemplateAttribute attribute in section.SurveyTemplateAttributes)
{
SurveyAttributeDetail AttributeDetail = new SurveyAttributeDetail();
AttributeDetail.AttributeName = attribute.AttributeName;
AttributeDetail.AttributeScale = attribute.AttributeScale;
AttributeDetail.AttributeType = attribute.AttributeType;
AttributeDetail.AttributeOrder = attribute.AttributeOrder;
AttributeDetail.SectionDetail = SectionDetail;
db.SurveyAttributeDetails.Add(AttributeDetail);
attributeDetails.Add(AttributeDetail);
db.SaveChanges();
}
}
您可以改进以下几点:
不要在每个Add()
上保存SaveChanges()
Add()
:
foreach (TeamMember TeamMember in TeamMembers) { ... // db.SaveChanges(); foreach (TeamMember peer in TeamMembers) { foreach (SurveySectionDetail SectionDetail in sectionDetails) { foreach (SurveyAttributeDetail AttributeDetail in attributeDetails.Where(a => a.SectionDetail.SurveySectionDetailID == SectionDetail.SurveySectionDetailID) ) { ... // db.SaveChanges(); } } } db.SaveChanges(); }
Include()
预加载您的导航属性; 要么 ToDictionary()
或ToLookup()
兑现部分或整个表 如果适合您的设置,请使用AddRange()
甚至是EntityFramework.BulkInsert中的 BulkInsert()
代替Add()
:
db.SurveyAnswers.AddRange( TeamMembers.SelectMany(p => sectionDetails.SelectMany(s => attributeDetails.Where(a => a.SectionDetail.SurveySectionDetailID == s.SurveySectionDetailID) .Select(a => new SurveyAnswer() { Reviewee = p, SurveyFormID = form.SurveyFormID, Detail = a, SectionDetail = s, }))));
使用Include
可以避免SELECT N + 1问题 。
SurveyTemplate template = db.SurveyTemplates.Include("SurveyTemplateSections")
.Include("SurveyTemplateSections.SurveyTemplateAttributes")
.First(x=> x.SurveyTemplateID == SurveyTemplateID);
生成整个对象图,然后保存到数据库。
List<SurveySectionDetail> sectionDetails = new List<SurveySectionDetail>();
List<SurveyAttributeDetail> attributeDetails = new List<SurveyAttributeDetail>();
foreach (SurveyTemplateSection section in template.SurveyTemplateSections)
{
SurveySectionDetail SectionDetail = new SurveySectionDetail();
//Some code
sectionDetails.Add(SectionDetail);
foreach (SurveyTemplateAttribute attribute in section.SurveyTemplateAttributes)
{
SurveyAttributeDetail AttributeDetail = new SurveyAttributeDetail();
//some code
attributeDetails.Add(AttributeDetail);
}
}
db.SurveySectionDetails.AddRange(sectionDetails);
db.SurveyAttributeDetails.AddRange(attributeDetails);
db.SaveChanges();
在循环之前加载所需的所有员工,这将避免对每个团队成员进行数据库查询。
var teamMemberIds = db.TeamMembers.Where(m => m.TeamID == TeamID && m.OnTeam)
.Select(x=>x.TeamMemberId).ToList();
var employees = db.Employees.Where(x => teamMemberIds.Contains(x.EmployeeId));
根据他们的sectionDetailId
为attributeDetails
创建字典,以避免在每次迭代时查询该列表。
var attributeDetailsGroupBySection = attributeDetails.GroupBy(x => x.SectionDetailId)
.ToDictionary(x => x.Key, x => x);
将保存SurveyAnswers
和SurveyForms
移到循环之外:
List<SurveyForm> forms = new List<SurveyForm>();
List<SurveyAnswer> answers = new List<SurveyAnswer>();
foreach (int teamMemberId in teamMemberIds)
{
var employee = employees.First(x => x.Id == teamMemberId);
SurveyForm form = new SurveyForm();
//some code
forms.Add(form);
foreach (int peer in teamMemberIds)
{
foreach (SurveySectionDetail SectionDetail in sectionDetails)
{
foreach (SurveyAttributeDetail AttributeDetail in
attributeDetailsGroupBySection[SectionDetail.Id])
{
SurveyAnswer answer = new SurveyAnswer();
//some code
answers.Add(answer);
}
}
}
}
db.SurveyAnswers.AddRange(answers);
db.SurveyForms.AddRange(forms);
db.SaveChanges();
最后,如果您想更快地插入,可以使用EntityFramework.BulkInsert 。 使用此扩展名,您可以像这样保存数据:
db.BulkInsert(answers);
db.BulkInsert(forms);
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.