简体   繁体   English

使用FirstOrDefault()怪异的LINQ to SQL超时问题

[英]Weird LINQ to SQL Timeout Issue using FirstOrDefault()

I have the following code which times out: 我有以下代码超时:

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

I profiled the SQL using SQL Server Profiler and grabbed the executed SQL. 我使用SQL Server Profiler对SQL进行了概要分析,并获取了已执行的SQL。 As expected, it contains a TOP 1. When I run the exact SQL in SQL Management Studio, it comes back in no time at all. 正如预期的那样,它包含一个TOP1。当我在SQL Management Studio中运行确切的SQL时,它很快就会回来。 Yet, it continues to timeout in the code. 但是,它在代码中继续超时。 Amazingly, changing it to the following works just fine: 令人惊讶的是,将其更改为以下内容就可以了:

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

I understand that now I am enumerating the results before applying the FirstOrDefault() to the now in-memory collection. 我知道现在在将FirstOrDefault()应用于当前内存中的集合之前,先枚举结果。 But it seems strange that executing the same SQL output in the first scenario directly in SQL Server had no problems. 但是,在第一种情况下直接在SQL Server中直接执行相同的SQL输出没有问题,这似乎很奇怪。

Can somebody maybe explain what is going on here? 有人可以解释这是怎么回事吗? In this instance, it was a group query that always returned one row regardless. 在这种情况下,无论如何,它都是始终返回一行的组查询。 So I am lucky that I can enumerate before applying FirstOrDefault(). 因此,我很幸运能够在应用FirstOrDefault()之前进行枚举。 But for possible future reference, what if that query returned thousands of rows to which I only wanted the TOP 1. 但是,为了将来提供参考,如果该查询返回了我只希望返回TOP 1的数千行,该怎么办?

ADDITION INFO 附加信息

The SQL using .FirstOrDefault(): 使用.FirstOrDefault()的SQL:

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

The SQL using .ToArray(): 使用.ToArray()的SQL:

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

Executing either directly in SQL Mgt Studio resulted in the same results in the same amount of time. 直接在SQL Mgt Studio中执行任一操作会在相同的时间内产生相同的结果。 However, when LINQ executes the first one, I get a timeout. 但是,当LINQ执行第一个时,我超时了。

This is a common problem when using linq to sql. 使用linq到sql时,这是一个常见问题。 If you think about sql, when you do a group by and then a firstordefault you're asking sql to aggregate and then unaggregate. 如果您考虑使用sql,则在进行group by然后是firstordefault时,您是在要求sql进行聚合,然后再进行非聚合。 It's hard for sql to deal with the individual elements in a group by since it'll be doing multiple queries to reach the individual elements. 对于sql来说,很难通过组来处理单个元素,因为它将执行多个查询以到达单个元素。

When you do ToArray, you're actually pulling the data back into memory and the group by is actually stored in memory with the individual elements so reaching these will be a lot faster. 当您执行ToArray时,实际上是将数据拉回到内存中,而分组依据实际上是与各个元素一起存储在内存中的,因此到达这些元素的速度会快得多。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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