2016-08-08 16 views
1

У меня довольно сложный запрос LINQ, который часто бывает настолько медленным, что он создает System.Data.SqlClient.SqlException: «Ожидаемая операция ожидания».LINQ query slow, создает Timeout; Сгенерированный SQL в порядке?

Однако при входе сгенерированный SQL (путем присвоения TextWriter к DataContext «ы Log), и выполнить его непосредственно на SQL Server, он завершает примерно 4 секунды, который является хорошо.

Откуда возникает несоответствие и как его отладить?

Редактировать: Я также заметил, что в Sql Server Management Studio отслеживает время процессора до 100%, когда запрос выполняется из .NET, но только 3% или около того при выполнении сгенерированного SQL-запроса.

Я не знаю, как разместить свой код поможет, но так как это было предложено, вот код, содержащий запрос:

var Db = MyProject.GetDataContext(); 
var statusPaymentSuccess = new string[] { "SUCCESS", "REMBOURS", "AFTERPAY" }; 

var items = Db.Orders.Where(item => 
    (siteid == null || item.SiteId == siteid) && 
    (ls_list.Contains(item.OrderOrderLifeCycles.OrderByDescending(it => it.Id).First().OrderLifeCycleId)) && 
    (item.OrderOrderPaymentStatus.Any(ops => statusPaymentSuccess.Contains(ops.OrderPaymentStatus.Code)) && 
     (CycleID == null || item.OrderOrderLifeCycles.First().OrderLifeCycleId == CycleID) && 
     (LocationID == null || item.SaleLocationId == LocationID) && 
     (string.IsNullOrEmpty(SalesPerson) || item.EmployeeName.ToLower() == SalesPerson.ToLower())) 
); 

var betweenorders = items.Select(it => new OrderBetween() 
{ 
    FirstPayDate = it.OrderOrderPaymentStatus.FirstOrDefault(ops => statusPaymentSuccess.Contains(ops.OrderPaymentStatus.Code)).DateTime, 
    OrderTotal = it.TotalAmount, 
    VatTotal = it.OrderItems.Sum(it2 => it2.BTWAmount ?? 0), 
    Quantity = it.OrderItems.Count, 
    SiteId = it.SiteId 
}); 

return betweenorders.Where(item => item.FirstPayDate >= start && item.FirstPayDate < stop) 
    .GroupBy(item => item.FirstPayDate.Value.Year + "-" + item.FirstPayDate.Value.Month).Select(
     item => 
      new SaleTotal() 
      { 
       Count = item.Sum(sub => sub.Quantity), 
       Month = item.FirstOrDefault().FirstPayDate.Value.Year + "-" + item.FirstOrDefault().FirstPayDate.Value.Month.ToString().PadLeft(2, '0'), 
       Total = item.Sum(sub => sub.OrderTotal), 
       VAT = item.Sum(sub => sub.VatTotal) 
      }).OrderBy(item => item.Month).ToArray(); 

где ls_list является List<int>, содержащим OrderOrderLifeCycles идентификаторов.

Сформированный запрос SQL, как вытащил из журнала:

DECLARE @p0 NVarChar(4000) = 'SUCCESS' 
DECLARE @p1 NVarChar(4000) = 'REMBOURS' 
DECLARE @p2 NVarChar(4000) = 'AFTERPAY' 
DECLARE @p3 Decimal(31,2) = '0' 
DECLARE @p4 NVarChar(4000) = '-' 
DECLARE @p5 DateTime = '2016-06-01' 
DECLARE @p6 DateTime = '2016-09-01' 
DECLARE @p7 Int = '4' 
DECLARE @p8 Int = '5' 
DECLARE @p9 Int = '8' 
DECLARE @p10 NVarChar(4000) = 'SUCCESS' 
DECLARE @p11 NVarChar(4000) = 'REMBOURS' 
DECLARE @p12 NVarChar(4000) = 'AFTERPAY' 
DECLARE @p13 NVarChar(4000) = '-' 
DECLARE @p14 NVarChar(4000) = '-' 
DECLARE @p15 Int = '2' 
DECLARE @p16 NChar(1) = '0' 
SELECT [t64].[value] AS [Month], [t64].[value2] AS [Count], [t64].[value22] AS [Total], [t64].[value3] AS [VAT] 
FROM (
    SELECT ((CONVERT(NVarChar,DATEPART(Year, (
     SELECT [t23].[value] 
     FROM (
      SELECT TOP (1) [t18].[value] 
      FROM (
       SELECT (
        SELECT [t17].[DateTime] 
        FROM (
         SELECT TOP (1) [t15].[DateTime] 
         FROM [dbo].[OrderOrderPaymentStatus] AS [t15] 
         INNER JOIN [dbo].[OrderPaymentStatus] AS [t16] ON [t16].[Id] = [t15].[OrderPaymentStatusId] 
         WHERE ([t16].[Code] IN (@p0, @p1, @p2)) AND ([t15].[OrderId] = [t14].[Id]) 
         ) AS [t17] 
        ) AS [value], [t14].[Id] 
       FROM [dbo].[Order] AS [t14] 
       ) AS [t18] 
      WHERE ((([t13].[value4] IS NULL) AND ((((CONVERT(NVarChar,DATEPART(Year, [t18].[value]))) + @p13) + (CONVERT(NVarChar,DATEPART(Month, [t18].[value])))) IS NULL)) OR (([t13].[value4] IS NOT NULL) AND ((((CONVERT(NVarChar,DATEPART(Year, [t18].[value]))) + @p13) + (CONVERT(NVarChar,DATEPART(Month, [t18].[value])))) IS NOT NULL) AND ([t13].[value4] = (((CONVERT(NVarChar,DATEPART(Year, [t18].[value]))) + @p13) + (CONVERT(NVarChar,DATEPART(Month, [t18].[value]))))))) AND ([t18].[value] >= @p5) AND ([t18].[value] < @p6) AND (((
       SELECT [t20].[OrderLifeCycleId] 
       FROM (
        SELECT TOP (1) [t19].[OrderLifeCycleId] 
        FROM [dbo].[OrderOrderLifeCycle] AS [t19] 
        WHERE [t19].[OrderId] = [t18].[Id] 
        ORDER BY [t19].[Id] DESC 
        ) AS [t20] 
       )) IN (@p7, @p8, @p9)) AND (EXISTS(
       SELECT NULL AS [EMPTY] 
       FROM [dbo].[OrderOrderPaymentStatus] AS [t21] 
       INNER JOIN [dbo].[OrderPaymentStatus] AS [t22] ON [t22].[Id] = [t21].[OrderPaymentStatusId] 
       WHERE ([t22].[Code] IN (@p10, @p11, @p12)) AND ([t21].[OrderId] = [t18].[Id]) 
       )) 
      ) AS [t23] 
     )))) + @p14) + (
     (CASE 
      WHEN (CONVERT(Int,DATALENGTH(CONVERT(NVarChar,DATEPART(Month, (
       SELECT [t63].[value] 
       FROM (
        SELECT TOP (1) [t58].[value] 
        FROM (
         SELECT (
          SELECT [t57].[DateTime] 
          FROM (
           SELECT TOP (1) [t55].[DateTime] 
           FROM [dbo].[OrderOrderPaymentStatus] AS [t55] 
           INNER JOIN [dbo].[OrderPaymentStatus] AS [t56] ON [t56].[Id] = [t55].[OrderPaymentStatusId] 
           WHERE ([t56].[Code] IN (@p0, @p1, @p2)) AND ([t55].[OrderId] = [t54].[Id]) 
           ) AS [t57] 
          ) AS [value], [t54].[Id] 
         FROM [dbo].[Order] AS [t54] 
         ) AS [t58] 
        WHERE ((([t13].[value4] IS NULL) AND ((((CONVERT(NVarChar,DATEPART(Year, [t58].[value]))) + @p13) + (CONVERT(NVarChar,DATEPART(Month, [t58].[value])))) IS NULL)) OR (([t13].[value4] IS NOT NULL) AND ((((CONVERT(NVarChar,DATEPART(Year, [t58].[value]))) + @p13) + (CONVERT(NVarChar,DATEPART(Month, [t58].[value])))) IS NOT NULL) AND ([t13].[value4] = (((CONVERT(NVarChar,DATEPART(Year, [t58].[value]))) + @p13) + (CONVERT(NVarChar,DATEPART(Month, [t58].[value]))))))) AND ([t58].[value] >= @p5) AND ([t58].[value] < @p6) AND (((
         SELECT [t60].[OrderLifeCycleId] 
         FROM (
          SELECT TOP (1) [t59].[OrderLifeCycleId] 
          FROM [dbo].[OrderOrderLifeCycle] AS [t59] 
          WHERE [t59].[OrderId] = [t58].[Id] 
          ORDER BY [t59].[Id] DESC 
          ) AS [t60] 
         )) IN (@p7, @p8, @p9)) AND (EXISTS(
         SELECT NULL AS [EMPTY] 
         FROM [dbo].[OrderOrderPaymentStatus] AS [t61] 
         INNER JOIN [dbo].[OrderPaymentStatus] AS [t62] ON [t62].[Id] = [t61].[OrderPaymentStatusId] 
         WHERE ([t62].[Code] IN (@p10, @p11, @p12)) AND ([t61].[OrderId] = [t58].[Id]) 
         )) 
        ) AS [t63] 
       ))))/2)) >= @p15 THEN CONVERT(NVarChar,DATEPART(Month, (
       SELECT [t63].[value] 
       FROM (
        SELECT TOP (1) [t58].[value] 
        FROM (
         SELECT (
          SELECT [t57].[DateTime] 
          FROM (
           SELECT TOP (1) [t55].[DateTime] 
           FROM [dbo].[OrderOrderPaymentStatus] AS [t55] 
           INNER JOIN [dbo].[OrderPaymentStatus] AS [t56] ON [t56].[Id] = [t55].[OrderPaymentStatusId] 
           WHERE ([t56].[Code] IN (@p0, @p1, @p2)) AND ([t55].[OrderId] = [t54].[Id]) 
           ) AS [t57] 
          ) AS [value], [t54].[Id] 
         FROM [dbo].[Order] AS [t54] 
         ) AS [t58] 
        WHERE ((([t13].[value4] IS NULL) AND ((((CONVERT(NVarChar,DATEPART(Year, [t58].[value]))) + @p13) + (CONVERT(NVarChar,DATEPART(Month, [t58].[value])))) IS NULL)) OR (([t13].[value4] IS NOT NULL) AND ((((CONVERT(NVarChar,DATEPART(Year, [t58].[value]))) + @p13) + (CONVERT(NVarChar,DATEPART(Month, [t58].[value])))) IS NOT NULL) AND ([t13].[value4] = (((CONVERT(NVarChar,DATEPART(Year, [t58].[value]))) + @p13) + (CONVERT(NVarChar,DATEPART(Month, [t58].[value]))))))) AND ([t58].[value] >= @p5) AND ([t58].[value] < @p6) AND (((
         SELECT [t60].[OrderLifeCycleId] 
         FROM (
          SELECT TOP (1) [t59].[OrderLifeCycleId] 
          FROM [dbo].[OrderOrderLifeCycle] AS [t59] 
          WHERE [t59].[OrderId] = [t58].[Id] 
          ORDER BY [t59].[Id] DESC 
          ) AS [t60] 
         )) IN (@p7, @p8, @p9)) AND (EXISTS(
         SELECT NULL AS [EMPTY] 
         FROM [dbo].[OrderOrderPaymentStatus] AS [t61] 
         INNER JOIN [dbo].[OrderPaymentStatus] AS [t62] ON [t62].[Id] = [t61].[OrderPaymentStatusId] 
         WHERE ([t62].[Code] IN (@p10, @p11, @p12)) AND ([t61].[OrderId] = [t58].[Id]) 
         )) 
        ) AS [t63] 
       ))) 
      ELSE REPLICATE(@p16, @p15 - (CONVERT(Int,DATALENGTH(CONVERT(NVarChar,DATEPART(Month, (
       SELECT [t63].[value] 
       FROM (
        SELECT TOP (1) [t58].[value] 
        FROM (
         SELECT (
          SELECT [t57].[DateTime] 
          FROM (
           SELECT TOP (1) [t55].[DateTime] 
           FROM [dbo].[OrderOrderPaymentStatus] AS [t55] 
           INNER JOIN [dbo].[OrderPaymentStatus] AS [t56] ON [t56].[Id] = [t55].[OrderPaymentStatusId] 
           WHERE ([t56].[Code] IN (@p0, @p1, @p2)) AND ([t55].[OrderId] = [t54].[Id]) 
           ) AS [t57] 
          ) AS [value], [t54].[Id] 
         FROM [dbo].[Order] AS [t54] 
         ) AS [t58] 
        WHERE ((([t13].[value4] IS NULL) AND ((((CONVERT(NVarChar,DATEPART(Year, [t58].[value]))) + @p13) + (CONVERT(NVarChar,DATEPART(Month, [t58].[value])))) IS NULL)) OR (([t13].[value4] IS NOT NULL) AND ((((CONVERT(NVarChar,DATEPART(Year, [t58].[value]))) + @p13) + (CONVERT(NVarChar,DATEPART(Month, [t58].[value])))) IS NOT NULL) AND ([t13].[value4] = (((CONVERT(NVarChar,DATEPART(Year, [t58].[value]))) + @p13) + (CONVERT(NVarChar,DATEPART(Month, [t58].[value]))))))) AND ([t58].[value] >= @p5) AND ([t58].[value] < @p6) AND (((
         SELECT [t60].[OrderLifeCycleId] 
         FROM (
          SELECT TOP (1) [t59].[OrderLifeCycleId] 
          FROM [dbo].[OrderOrderLifeCycle] AS [t59] 
          WHERE [t59].[OrderId] = [t58].[Id] 
          ORDER BY [t59].[Id] DESC 
          ) AS [t60] 
         )) IN (@p7, @p8, @p9)) AND (EXISTS(
         SELECT NULL AS [EMPTY] 
         FROM [dbo].[OrderOrderPaymentStatus] AS [t61] 
         INNER JOIN [dbo].[OrderPaymentStatus] AS [t62] ON [t62].[Id] = [t61].[OrderPaymentStatusId] 
         WHERE ([t62].[Code] IN (@p10, @p11, @p12)) AND ([t61].[OrderId] = [t58].[Id]) 
         )) 
        ) AS [t63] 
       ))))/2))) + (CONVERT(NVarChar,DATEPART(Month, (
       SELECT [t63].[value] 
       FROM (
        SELECT TOP (1) [t58].[value] 
        FROM (
         SELECT (
          SELECT [t57].[DateTime] 
          FROM (
           SELECT TOP (1) [t55].[DateTime] 
           FROM [dbo].[OrderOrderPaymentStatus] AS [t55] 
           INNER JOIN [dbo].[OrderPaymentStatus] AS [t56] ON [t56].[Id] = [t55].[OrderPaymentStatusId] 
           WHERE ([t56].[Code] IN (@p0, @p1, @p2)) AND ([t55].[OrderId] = [t54].[Id]) 
           ) AS [t57] 
          ) AS [value], [t54].[Id] 
         FROM [dbo].[Order] AS [t54] 
         ) AS [t58] 
        WHERE ((([t13].[value4] IS NULL) AND ((((CONVERT(NVarChar,DATEPART(Year, [t58].[value]))) + @p13) + (CONVERT(NVarChar,DATEPART(Month, [t58].[value])))) IS NULL)) OR (([t13].[value4] IS NOT NULL) AND ((((CONVERT(NVarChar,DATEPART(Year, [t58].[value]))) + @p13) + (CONVERT(NVarChar,DATEPART(Month, [t58].[value])))) IS NOT NULL) AND ([t13].[value4] = (((CONVERT(NVarChar,DATEPART(Year, [t58].[value]))) + @p13) + (CONVERT(NVarChar,DATEPART(Month, [t58].[value]))))))) AND ([t58].[value] >= @p5) AND ([t58].[value] < @p6) AND (((
         SELECT [t60].[OrderLifeCycleId] 
         FROM (
          SELECT TOP (1) [t59].[OrderLifeCycleId] 
          FROM [dbo].[OrderOrderLifeCycle] AS [t59] 
          WHERE [t59].[OrderId] = [t58].[Id] 
          ORDER BY [t59].[Id] DESC 
          ) AS [t60] 
         )) IN (@p7, @p8, @p9)) AND (EXISTS(
         SELECT NULL AS [EMPTY] 
         FROM [dbo].[OrderOrderPaymentStatus] AS [t61] 
         INNER JOIN [dbo].[OrderPaymentStatus] AS [t62] ON [t62].[Id] = [t61].[OrderPaymentStatusId] 
         WHERE ([t62].[Code] IN (@p10, @p11, @p12)) AND ([t61].[OrderId] = [t58].[Id]) 
         )) 
        ) AS [t63] 
       )))) 
     END)) AS [value], [t13].[value] AS [value2], [t13].[value2] AS [value22], [t13].[value3] 
    FROM (
     SELECT SUM([t8].[value3]) AS [value], SUM([t8].[TotalAmount]) AS [value2], SUM([t8].[value22]) AS [value3], [t8].[value] AS [value4] 
     FROM (
      SELECT ((CONVERT(NVarChar,DATEPART(Year, [t7].[value]))) + @p4) + (CONVERT(NVarChar,DATEPART(Month, [t7].[value]))) AS [value], [t7].[value] AS [value2], [t7].[Id], [t7].[value3], [t7].[TotalAmount], [t7].[value2] AS [value22] 
      FROM (
       SELECT (
        SELECT [t3].[DateTime] 
        FROM (
         SELECT TOP (1) [t1].[DateTime] 
         FROM [dbo].[OrderOrderPaymentStatus] AS [t1] 
         INNER JOIN [dbo].[OrderPaymentStatus] AS [t2] ON [t2].[Id] = [t1].[OrderPaymentStatusId] 
         WHERE ([t2].[Code] IN (@p0, @p1, @p2)) AND ([t1].[OrderId] = [t0].[Id]) 
         ) AS [t3] 
        ) AS [value], [t0].[TotalAmount], (
        SELECT SUM([t5].[value]) 
        FROM (
         SELECT COALESCE([t4].[BTWAmount],@p3) AS [value], [t4].[OrderId] 
         FROM [dbo].[OrderItem] AS [t4] 
         ) AS [t5] 
        WHERE [t5].[OrderId] = [t0].[Id] 
        ) AS [value2], (
        SELECT COUNT(*) 
        FROM [dbo].[OrderItem] AS [t6] 
        WHERE [t6].[OrderId] = [t0].[Id] 
        ) AS [value3], [t0].[Id] 
       FROM [dbo].[Order] AS [t0] 
       ) AS [t7] 
      ) AS [t8] 
     WHERE ([t8].[value2] >= @p5) AND ([t8].[value2] < @p6) AND (((
      SELECT [t10].[OrderLifeCycleId] 
      FROM (
       SELECT TOP (1) [t9].[OrderLifeCycleId] 
       FROM [dbo].[OrderOrderLifeCycle] AS [t9] 
       WHERE [t9].[OrderId] = [t8].[Id] 
       ORDER BY [t9].[Id] DESC 
       ) AS [t10] 
      )) IN (@p7, @p8, @p9)) AND (EXISTS(
      SELECT NULL AS [EMPTY] 
      FROM [dbo].[OrderOrderPaymentStatus] AS [t11] 
      INNER JOIN [dbo].[OrderPaymentStatus] AS [t12] ON [t12].[Id] = [t11].[OrderPaymentStatusId] 
      WHERE ([t12].[Code] IN (@p10, @p11, @p12)) AND ([t11].[OrderId] = [t8].[Id]) 
      )) 
     GROUP BY [t8].[value] 
     ) AS [t13] 
    ) AS [t64] 
ORDER BY [t64].[value] 
+0

Сколько строк делает возвращение запроса? Является ли сервер локальной или локальной? – user3185569

+0

@Preotector - Я думаю, что это хорошая идея добавить запрос, чтобы мы могли попытаться понять, что может заставить его замедлить (и, как вы сказали, не из-за того, что linq вы решили делать, а как в том, как он взаимодействует с БД) –

+2

попробуйте сравнить план выполнения с помощью SQL Profiler. Сначала запустите команду linq и просмотрите план выполнения, после чего запустите свой запрос в SSMS и посмотрите план выполнения ... Есть несколько вещей, которые отличаются в зависимости от контекста выполнения (ARTHABORT ON/OFF является одним из них, но, не глядя на ваш запрос, мы не можем быть уверены, что ваш запрос должен что-то сделать с этим) ... но сравнение планов выполнения даст вам хорошее представление о том, где вещи slow – Nirman

ответ

2

Один очевидный способ улучшить ваш запрос бросается в глаза сразу же в первой части:

var items = Db.Orders.Where(item => 
    (siteid == null || item.SiteId == siteid) 
&& (ls_list.Contains(item.OrderOrderLifeCycles.OrderByDescending(it => it.Id).First().OrderLifeCycleId)) 
&& (item.OrderOrderPaymentStatus.Any(ops => statusPaymentSuccess.Contains(ops.OrderPaymentStatus.Code)) 
&& (CycleID == null || item.OrderOrderLifeCycles.First().OrderLifeCycleId == CycleID) 
&& (LocationID == null || item.SaleLocationId == LocationID) 
&& (string.IsNullOrEmpty(SalesPerson) || item.EmployeeName.ToLower() == SalesPerson.ToLower())) 
); 

Помните, что в целом заявление LINQ переводится на SQL, включая все эти проверки null. Это делает запрос SQL излишне сложным и сложнее обрабатывать оптимизатором запросов. (Кстати, вы показываете SQL-запрос, принадлежащий другому оператору LINQ).

Рекомендуемый способ борьбы с обнуляемыми условиями для составления запроса:

IQueryable<Order> items = var items = Db.Orders; 

if(siteid != null) 
{ 
    items = items.Where(item => item.SiteId == siteid); 
} 
if (CycleID != null) 
{ 
    items = items.Where(item => item.OrderOrderLifeCycles.First().OrderLifeCycleId == CycleID); 
} 
// etc. 

Другого дело

item.EmployeeName.ToLower() == SalesPerson.ToLower() 

Это преобразует EmployeeName значения полей перед условием поиска применяется к нему. Это означает, что любой индекс на EmployeeName не может быть использован (также известен как не поддается определению). I думаю вы можете удалить звонки ToLower(). В SQL-запросе используется сопоставление базы данных поля EmployeeName, и по умолчанию это может быть нечувствительным к регистру (CI).

Наконец, вы можете рассмотреть, чтобы выполнить группировку ...

GroupBy(item => item.FirstPayDate.Value.Year + "-" + item.FirstPayDate.Value.Month) 

... в памяти (LINQ к объектам), а не в базе данных. То есть:

return betweenorders.Where(item => item.FirstPayDate >= start && item.FirstPayDate < stop) 
.AsEnumerable() // Switch to LINQ to objects 
.GroupBy(... 

группировка переводится как ORDER BY (не GROUP BY по причинам, выходящим за рамки этого ответа) и, опять же, преобразование поля базы данных FirstPayDate отключающего индексов. Это также делает запрос SQL менее сложным, и, вероятно, это не тяжелая операция для этого в памяти.

+0

Ничего себе, большие идеи. Благодаря! Но как вы знаете, что запрос относится к другому выражению LINQ? (Это не должно, но я думаю, что я мог бы небрежно с моим копированием вставить, внимательно глядя на SQL в течение целого дня.) –

+0

Кроме того, вы знаете хороший ресурс, чтобы найти такие «рекомендуемые способы» в LINQ/SQL? –

+1

Неверный запрос - я не вижу 'CycleID' и т. Д. В SQL. Что касается документации, места для запуска находятся здесь: https://msdn.microsoft.com/en-us/data/hh949853.aspx, https://www.simple-talk.com/dotnet/net-tools/entity- framework-performance-and-what-you-can-do-about-it/ –

0

Часть несоответствия объясняется в this SA ответ:

SSMS обычно использует в ARITHABORT ON и код обычно использует ARITHABORT OFF - это в основном вариант для того, как обрабатывать то, что происходит, если математическое строка в коде имеет ошибку - например разделите на ноль.

Главное, однако, состоит в том, что оба метода имеют другой план выполнения - вот почему одно и то же может (произвольно) брать много дольше на веб-сайте, чем в SSMS.

планов выполнения составляется на основе оценок впервые он используется так, что вы случайно обнаружите, что план выполнения является кэшируется в ужасном способе, который подходит ваш первый запрос, но это ужасно для последующих запросов. Вот что произошло здесь, и именно поэтому он только что начал работать снова - новый план запроса был создан после того, как была изменена хранимая процедура.

Выполнение запроса в SSMS с помощью SET ARITHABORT OFF уже значительно замедляет выполнение запроса. Тем не менее, он по крайней мере на 300% быстрее, чем версия LINQ-to-SQL в коде, поэтому я буду обновлять этот ответ больше, если найду его.

Редактировать: SSMS не имеет отношения к отслеживанию объектов, как это делает LINQ, поэтому при обработке запросов, которые только читают (против записи), мы можем ускорить выполнение LINQ-to-SQL, отключив отслеживание объектов. Затем вам нужно вручную указать, какие объекты загружать, но, насколько мне известно, может быть определено только путем проб и ошибок.
Для моего запроса, отключение отслеживания и загрузки объекта может быть обработано с помощью этого кода:

db.ObjectTrackingEnabled = false; 
var lo = new DataLoadOptions(); 
lo.LoadWith<Order>(x => x.OrderOrderPaymentStatus); 
lo.LoadWith<OrderOrderPaymentStatus>(x => x.OrderPaymentStatus); 
db.LoadOptions = lo;