优化慢LINQ查询

时间:2015-11-03 11:30:41

标签: c# performance entity-framework linq

我有一个LINQ查询,我在优化时遇到问题,需要大约5.5秒才能运行。我正在使用一个名为StaffingResourceData的视图和一个名为StaffingForecasts的表。

每个StaffingResource都有一个ResourceId,一个Division和一个Type。 StaffingForecast有一个ResourceId,Project,Date(表示一周的星期一),Hours。 StaffingResource可以有0多个StaffingForecasts。

对于每个StaffingResource,我需要一份他们未来12周的总预测时间列表。这就是我现在所拥有的:

// Get list of dates
var dates = new List<DateTime>();
var start = Utilities.GetStartOfWeek(DateTime.Today);
for (var i = 0; i < 12; i++)
{
    dates.Add(start.AddDays(i * 7));
}
var end = dates[11];

// Get resources
var resources = (from r in context.StaffingResourceDatas
                 where r.EmployeeId != null
                     && !exclusionList.Contains(r.ResourceTitleId)
                 join f in context.StaffingForecasts.Where(x => x.Date >= start && x.Date <= end) on r.ResourceId equals f.ResourceId into g1
                 from f in g1.DefaultIfEmpty()
                 group new { f.Date, f.Hours } by r into g2
                 select new ChartResourceModel
                 {
                     ResourceId = g2.Key.ResourceId,
                     Division = g2.Key.ResourceDivision,
                     Type = g2.Key.ResourceType,
                     Dates = dates.Select(d => new ChartDateModel
                     {
                         Date = d,
                         Available = (g2.Where(f => f.Date == d).Any() ? g2.Where(f => f.Date == d).Sum(f => f.Hours) : 0) < 24
                     }).ToList()
                 })
               .ToList();

关于如何加快速度的任何想法?

5 个答案:

答案 0 :(得分:6)

  1. 避免使用Contains。它严重降低了性能。 See this post

  2. ToList()是执行查询的命令。直到你调用ToList()方法,linq查询才会启动,因为linq有一个名为延迟执行的功能。因此,如果您调用ToList(),则可以使用Databaseof文件启动一些实际操作。

  3. 减少表的列数会减少所需的带宽(从查询中删除不必要的列)
  4. 关闭更改跟踪和身份管理(例如,LINQ-to-SQL中的ObjectTrackingEnabled)

    using (YourDataContext dataContext = new YourDataContext())    
    {
        dataContext.ObjectTrackingEnabled = false;    
        //Your code
    }
    
  5. 使用EF的其中一个调整选项,例如.AsNoTracking()The extensive description can be seen here.
  6. 使用预编译的查询。它有时会减少预处理开销

答案 1 :(得分:1)

在玩了一段时间之后,我能够将加载时间从5.5秒降低到1.5秒。以下是我提出的建议:

// Get resources
var resources = (from r in
                    (from r in context.StaffingResourceDatas
                     where r.EmployeeId != null
                         && !exclusionList.Contains(r.ResourceTitleId)
                     join f in context.StaffingForecasts on r.ResourceId equals f.ResourceId
                     group f by r into g
                     select new
                     {
                         Resource = g.Key,
                         Forecasts = g.Where(f => f.Date >= start && f.Date <= end && f.StaffingPotentialProject == null).ToList()
                     }).ToList()
                 group r.Forecasts by r.Resource into g
                 select new ChartResourceModel
                 {
                     ResourceId = g.Key.ReportsToId,
                     Division = g.Key.ResourceDivision,
                     Type = g.Key.ResourceType,
                     Dates = dates.Select(d => new ChartDateModel
                     {
                         Date = d,
                         Available = (g.SelectMany(f => f.Where(x => x.Date == d)).Sum(x => x.Hours)) < 24
                     }).ToList()
                 }).ToList();

似乎最好的方法是获取所需的所有数据并调用.ToList()而不尝试做任何奇特的事情,然后对该数据执行任何额外的操作。

答案 2 :(得分:0)

首先,尽量避免使用&#34; .ToList()&#34;除非一切都已完成,因为当您触发&#34; .ToList()&#34;时,结果正在实现,如果有大量数据并且您想要执行更多查询操作,则不需要关于那些数据。

因此,尝试使用IQueryable属性,这样您至少可以更快地获取数据,然后对其进行一些操作。

当然,检查您要发送给SQL的查询/查询是什么。也许你正在搜索的列没有正确编入索引(!?),或者你没有表中的任何索引(?)。

答案 3 :(得分:0)

  • 避免使用.ToList(),这在扩展或使用相关实体时始终会创建一个SQL查询
  • CompiledQueries提前编译查询,避免一些开销(不多)
  • 调试您的查询(例如,使用http://miniprofiler.com/

答案 4 :(得分:0)

我会把我的赌注放在一个简单的子查询上,“自然地”代表所需的信息,比如这个

var query =
    from r in context.StaffingResourceDatas
    where r.EmployeeId != null && !exclusionList.Contains(r.ResourceTitleId)
    select new ChartResourceModel
    {
        ResourceId = r.ResourceId,
        Division = r.ResourceDivision,
        Type = r.ResourceType,
        Dates = dates.Select(d => new ChartDateModel
        {
            Date = d,
            Available = context.StaffingForecasts.Where(f => 
                f.ResourceId == r.ResourceId && f.Date == d).Sum(f => f.Hours) < 24
        }).ToList()
    };
var sqlQuery = query.ToString();
var result = query.ToList();