繁体   English   中英

使用Entity Framework嵌套循环的运行时间非常慢(使用nav属性)

[英]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();  
                        }
                    }

您可以改进以下几点:

  1. 不要在每个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(); } 
  2. 考虑减少往返数据库的次数。 这可以通过以下方式完成: 它们占用大量内存
    • 使用Include()预加载您的导航属性; 要么
    • 使用ToDictionary()ToLookup()兑现部分或整个表
  3. 如果适合您的设置,请使用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));

根据他们的sectionDetailIdattributeDetails创建字典,以避免在每次迭代时查询该列表。

var attributeDetailsGroupBySection = attributeDetails.GroupBy(x => x.SectionDetailId)
       .ToDictionary(x => x.Key, x => x);

将保存SurveyAnswersSurveyForms移到循环之外:

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM