实体框架Linq foreach性能与使用Select()替代

时间:2018-07-12 23:56:13

标签: c# sql-server entity-framework linq out-of-memory

我的C#代码中有以下linq查询,这导致System.OutOfmemory excpetion

public SortedSet<string> GetstudentCount()
{
    var studentCount= studentRepository
                  .GetBy(i => i.IsStudentEnabled && i.IsEnrolledAllSubjects) 
                  .AsQueryable()
                  .AsNoTracking()
                  .ToList();

    var studentSortedSet= new SortedSet<string>();
    foreach (var student in studentCount)
    {
        var id = string.Format("{0}:{1}", student.studentFirstName, student.totalScore);
        studentSortedSet.Add(id);
    }

     return new SortedSet<string>(studentCount);
}

所以我正在尝试对其进行优化,并且可以看到这些选项,但是我不确定,因为在开发数据库中我没有足够的数据来测试。我是Entity Framework和Linq的新手,这让我感到困惑,无法确定哪种方法正确。

1)在Linq查询中删除了ToList(),但是foreach()花费的时间与以前相同(仍然很慢)

2)尝试删除整个foreach(),并在Linq查询本身中添加了Select(),例如:

public SortedSet<string> GetStudentCount()
{
    var studentCount= studentRepository
                          .GetBy(i => i.IsStudentEnabled && 
                           i.IsEnrolledAllSubjects)
                          .Select(string.Format("{0}:{1}",                          
                           student.studentFirstName, student.totalScore)) 
                          .AsQueryable()
                          .AsNoTracking()
                          .ToList();

      return new SortedSet<string>(studentCount);
}

但这甚至需要花费相同的时间(仍然很慢)

我进一步想到了在这里删除ToList(),但是这种方法在很多地方都可以使用(可以确认studentCount上没有循环),我不确定这是否可能导致更多问题。

关于此的任何建议/建议都更有意义。

EDIT2:

public IEnumerable<T> GetBy(Expression<Func<T, bool>> predicate)
{
    return dbSet.Where(predicate);
}

编辑:

对许多人来说,这可能是一个基本问题,我要求他们不要对此问题投反对票,因为在这里,我试图获得一些有关如何优化的建议。这里没有难过的感觉。我正在尝试学习这些知识,如果可以的话,我将不胜感激。谢谢

2 个答案:

答案 0 :(得分:2)

您可以做一些优化的事情。但是首先让我们先回顾一些问题。

不要使用存储库anti-pattern,您只是在为实体框架所做的事情编写包装器。

不要将所有内容都拉到内存中,如果遇到内存不足异常(假设您在其他地方的代码中没有做错任何事情),则将太多数据拉到内存中,如果需要提取那么多数据。创建一个paging api

仅从数据库中选择所需的数据。正如您已经发现的那样,仅在需要“名字”和“总分”时撤回整个数据集是一种浪费。

选择更好的数据结构。没有理由使用一组排序的字符串。同样非常令人怀疑的是,您会得到想要的结果,因为得分低一位数的人的排名会更高,因为您使用的是alpha排序,例如

Andy:93
Andy:8
Andy:79    

您的排序可能应该在SQL端完成 当所有这些都完成后,您应该有一个类似于以下内容(减去分页):

 public class StudentScores
 {
     public string Name { get; set;}
     public string TotalScore {get; set; }
 }

 var results = dbContext.Students.AsNoTracking().Where(s => s.IsStudentEnabled 
                                             && s.IsEnrolledAllSubjects)
                .OrderBy(x => x.studentFirstName).ThenBy(x => x.totalScore)
                .Select(x => new StudentScores { 
                                       Name = x.studentFirstName,
                                       TotalScore = x.totalScore
                }).ToList();

您还可以进行其他微优化,例如编译后的查询,但Id只是从现在开始。

(PS。我的主要猜测是错误是由于您没有向我们显示某些内容,似乎您没有向我们显示完整代码,因为此行中的学生变量来自何处     .Select(string.Format("{0}:{1}", student.studentFirstName, student.totalScore))

答案 1 :(得分:2)

  1. 避免在大查询中使用linq,而是使用存储过程或函数。
  2. 使用页面拆分查询:YourFunction(...,int pageSize = 50, int page = 0),然后是Get(...).Skip(pageSize *page).Take(pageSize)。没有页面将显示100多个结果
  3. 如果是网络应用,请拆分数据和页面,数据将被ajax加载
  4. 使用CompiledQuery.Compile:https://www.codeproject.com/Articles/38174/How-to-improve-your-LINQ-query-performance-by-X
  5. 使用Parallel queryyield return
  6. 尝试使用此帖子中提到的RoslynLinqRewrite或LinqOptimiser:http://mattwarren.org/2016/09/29/Optimising-LINQ/
  7. 更多性能细节: