linq查询占用了太多时间。需要减少时间

时间:2014-07-03 06:09:43

标签: c# performance linq linq-to-sql

这里我使用下面的查询,并且花费大约14到15秒的时间来检索大量数据。 在下面的Query中,CreatedDate是DateTimeOffset数据类型。

var naId = UnitOfWork.SalesPhases.FirstOrDefault(p => p.PhaseName =="NA").SalesPhaseId;
var rejectedId = UnitOfWork.SalesPhases.FirstOrDefault(p => p.PhaseName =="Rejected").SalesPhaseId;

var data = UnitOfWork.Leads.Query().AsEnumerable()
.Where(p =>(p.SalesPhaseId == naId || p.SalesPhaseId == rejectedId) &&
p.CreatedDate.Date >= fromDate && p.CreatedDate.Date <= toDate).Select(m =>
        new
        {
        m.LeadId,
        m.LeadOwnerId,
        m.SalesPhaseId,
        m.LeadActivities,
        m.Employee,
        m.SalesPhase,
        m.CompanyName,
        m.CreatedDate,
        m.LeadHistories,
        m.LeadAddresses
        }).ToList();

我尝试使用AsQueryable而不是AsEnumerable,但它给出了以下错误:

“LINQ to Entities不支持指定的类型成员'Date'。仅支持初始值设定项,实体成员和实体导航属性。”

你能帮我减少查询的执行时间吗?

3 个答案:

答案 0 :(得分:2)

您对AsEnumerable的使用迫使过滤在本地完成。它会将所有数据拉入其中,然后在您的应用中对其进行过滤。这显然非常低效。现在,您的查询的一部分似乎无法直接在LINQ to SQL中表达。我在这里看到两个选择。

首先,您可以在SQL中进行大多数过滤,但随后在本地进行日期过滤:

var data = UnitOfWork.Leads.Query()
                     // Do this part of the query in SQL
                     .Where(p => p.SalesPhaseId == naId || 
                                 p.SalesPhaseId == rejectedId) 
                     .AsEnumerable()
                     // Do the rest of the query in-process
                     .Where(p => p.CreatedDate.Date >= fromDate &&
                                 p.CreatedDate.Date <= toDate)
                     .Select(...)

如果第一部分将大量过滤掉它,那么它是合适的,然后你只需要对一小组数据进行本地处理。

或者,您可以根据DateTime计算出日期过滤的含义。看起来你可以做到:

// This may not be required, depending on the source.
fromDate = fromDate.Date;
// This will be, although you may be able to get rid of the ".Date" part.
toDate = toDate.Date.AddDays(1);

var data = UnitOfWork.Leads.Query()
                     // Do this part of the query in SQL
                     .Where(p => (p.SalesPhaseId == naId || 
                                  p.SalesPhaseId == rejectedId) &&
                                 p.CreatedDate >= fromDate &&
                                 p.CreatedDate < toDate)
                     .Select(...)

它创建了等效查询,但未在查询本身中使用Date属性。

答案 1 :(得分:0)

AsEnumerable()之后的所有内容在本地执行而不是在服务器上执行。另见

https://stackoverflow.com/a/2013876/141172

这意味着表中的所有行都是从数据库返回的,然后在C#代码中进行过滤。

删除该调用,以便在服务器端进行过滤。

编辑

注意到Jon的评论,它提醒我他重新实现了LINQ to Objects作为学习练习。他关于AsEnumerable()重新实现的评论值得一读

  

我可以很容易地描述它的行为:它返回源。

     

就是这样。没有参数验证,它不会创建另一个迭代器。它只是返回源。

     

你可能想知道重点是什么......而且都是关于改变表达式的编译时类型。我将在另一篇文章中讨论IQueryable(虽然可能没有实现与之相关的任何内容),但希望你知道它通常用于“进程外”查询 - 最常见的是在数据库中。

     

现在,想要在数据库中执行查询的某些方面,然后在.NET中进行更多操作并不是完全不常见的 - 特别是如果有些方面你基本上无法在LINQ to SQL(或任何提供程序)中实现你正在使用)。例如,您可能希望构建一个特定的内存表示形式,该表示形式不适合提供者的模型。

https://msmvps.com/blogs/jon_skeet/archive/2011/01/14/reimplementing-linq-to-objects-part-36-asenumerable.aspx

答案 2 :(得分:0)

您的代码应该是这样的..

var naId = UnitOfWork.SalesPhases.FirstOrDefault(p => p.PhaseName =="NA").SalesPhaseId;
var rejectedId = UnitOfWork.SalesPhases.FirstOrDefault(p => p.PhaseName =="Rejected").SalesPhaseId;

var data = UnitOfWork.Leads.Query().AsQueryable()
.Where(p =>(p.SalesPhaseId == naId || p.SalesPhaseId == rejectedId) &&
p.CreatedDate>= fromDate.Date && p.CreatedDate <= toDate.Date).Select(m =>
    new
    {
    m.LeadId,
    m.LeadOwnerId,
    m.SalesPhaseId,
    m.LeadActivities,
    m.Employee,
    m.SalesPhase,
    m.CompanyName,
    m.CreatedDate,
    m.LeadHistories,
    m.LeadAddresses
    }).ToList();

首先,您需要使用.ToQueryable而不是.ToIEnumerable()。 其次,您不能在实体框架linq查询中使用.Date到datetime属性。这仅适用于列表和数组等内存中的集合。