我有以下代码超时:
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执行第一个时,我会超时。
答案 0 :(得分:0)
使用linq to sql时这是一个常见问题。如果你考虑sql,当你做一个group by然后first firstdedefault你要求sql聚合然后unaggregate。 sql很难处理组中的各个元素,因为它会进行多个查询以达到各个元素。
当你进行ToArray时,你实际上是将数据拉回到内存中,而group by实际上存储在具有各个元素的内存中,因此达到这些要快得多。