使用FirstOrDefault()发生奇怪的LINQ to SQL超时问题

时间:2012-03-15 15:52:09

标签: c# linq-to-sql

我有以下代码超时:

        using (var ts = new TransactionScope(TransactionScopeOption.Required, new TransactionOptions { IsolationLevel = System.Transactions.IsolationLevel.ReadUncommitted }))
        {
            ECWSDataContext dc = new ECWSDataContext();

            IQueryable<Ticket> results = dc.Tickets;
            Business.TicketStatistic statistic = results
                .Select(r => new
                {
                    GroupID = 1,
                    IsVoided = r.IsVoided ? 1 : 0,
                    IsWarning = r.TicketFilingTypeID == 5 ? 1 : 0,
                    TotalFelonies = r.TotalFelonies,
                    TotalMisdemeanors = r.TotalMisdemeanors,
                    TotalInfractions = r.TotalInfractions,
                    TotalOrdinances = r.TotalOrdinances,
                    TotalWarnings = r.TotalWarnings
                })
                .GroupBy(t => t.GroupID)
                .Select(g => new Business.TicketStatistic()
                {
                    TotalTickets = g.Count(),
                    TotalVoids = g.Sum(x => x.IsVoided),
                    TotalTicketWarnings = g.Sum(x => x.IsWarning),
                    TotalFelonies = g.Sum(x => x.TotalFelonies),
                    TotalMisdemeanors = g.Sum(x => x.TotalMisdemeanors),
                    TotalInfractions = g.Sum(x => x.TotalInfractions),
                    TotalOrdinances = g.Sum(x => x.TotalOrdinances),
                    TotalOffenseWarnings = g.Sum(x => x.TotalWarnings)
                }).FirstOrDefault();
        }

我使用SQL Server Profiler分析了SQL并抓取了执行的SQL。正如预期的那样,它包含一个TOP 1.当我在SQL Management Studio中运行确切的SQL时,它会立即返回。然而,它在代码中继续超时。令人惊讶的是,将其更改为以下工作就可以了:

        using (var ts = new TransactionScope(TransactionScopeOption.Required, new TransactionOptions { IsolationLevel = System.Transactions.IsolationLevel.ReadUncommitted }))
        {
            ECWSDataContext dc = new ECWSDataContext();

            IQueryable<Ticket> results = dc.Tickets;

            var stats = results
                .Select(r => new
                {
                    GroupID = 1,
                    IsVoided = r.IsVoided ? 1 : 0,
                    IsWarning = r.TicketFilingTypeID == 5 ? 1 : 0,
                    TotalFelonies = r.TotalFelonies,
                    TotalMisdemeanors = r.TotalMisdemeanors,
                    TotalInfractions = r.TotalInfractions,
                    TotalOrdinances = r.TotalOrdinances,
                    TotalWarnings = r.TotalWarnings
                })
                .GroupBy(t => t.GroupID)
                .Select(g => new Business.TicketStatistic()
                {
                    TotalTickets = g.Count(),
                    TotalVoids = g.Sum(x => x.IsVoided),
                    TotalTicketWarnings = g.Sum(x => x.IsWarning),
                    TotalFelonies = g.Sum(x => x.TotalFelonies),
                    TotalMisdemeanors = g.Sum(x => x.TotalMisdemeanors),
                    TotalInfractions = g.Sum(x => x.TotalInfractions),
                    TotalOrdinances = g.Sum(x => x.TotalOrdinances),
                    TotalOffenseWarnings = g.Sum(x => x.TotalWarnings)
                }).ToArray();

            Business.TicketStatistic statistic = stats.FirstOrDefault();
        }

我知道现在我在将FirstOrDefault()应用到现在的内存中集合之前枚举结果。但是直接在SQL Server中在第一个场景中执行相同的SQL输出没有问题,这似乎很奇怪。

有人可以解释一下这里发生了什么吗?在这个例子中,它是一个总是返回一行的组查询。所以我很幸运,我可以在应用FirstOrDefault()之前进行枚举。但是为了将来可能参考,如果该查询返回了数千行,我只想要TOP 1。

添加信息

使用.FirstOrDefault()的SQL:

SELECT TOP 1 Field1, Field2...
FROM
(
    SELECT SUM(Field) as Field1, ...
    FROM ...
) SUB

使用.ToArray()的SQL:

SELECT SUM(Field) as Field1, ...
FROM ...

直接在SQL中执行Mgt Studio会在相同的时间内产生相同的结果。但是,当LINQ执行第一个时,我会超时。

1 个答案:

答案 0 :(得分:0)

使用linq to sql时这是一个常见问题。如果你考虑sql,当你做一个group by然后first firstdedefault你要求sql聚合然后unaggregate。 sql很难处理组中的各个元素,因为它会进行多个查询以达到各个元素。

当你进行ToArray时,你实际上是将数据拉回到内存中,而group by实际上存储在具有各个元素的内存中,因此达到这些要快得多。