Запрос Entity Framework Core 3.0 вызывает «SqlException:« Время выполнения истекло »» и tempdb заполняются. Работает с EF Core 2.2.6 - PullRequest
0 голосов
/ 12 октября 2019

Я выполняю довольно простой запрос в Microsoft Entity Framework Core 3.0, который выглядит следующим образом:

var dbProfile = db.Profiles.Where(x => x.SiteId == Int32.Parse(id))
    .Include(x => x.Interests)
    .Include(x => x.Pets)
    .Include(x => x.Networks)
    .Include(x => x.PersonalityTraits)
    .SingleOrDefault();

Он отлично работал с EF Core 2.2.6, но при обновлении до EF Core 3.0 этот запросвыполняется мгновенно для профилей 721, но по крайней мере для одного профиля тайм-аут запроса:

Microsoft.Data.SqlClient.SqlException: 'Тайм-аут выполнения истек.
Период тайм-аута истек до завершенияили сервер не отвечает. '

Затем я зарегистрировал фактический запрос, отправленный на сервер базы данных:

https://stackoverflow.com/a/58348159/3850405

SELECT [t].[Id], [t].[Age], [t].[City], [t].[Country], [t].[County], [t].[DeactivatedAccount], [t].[Gender], [t].[HasPictures], [t].[LastLogin], [t].[MemberSince], [t].[PresentationUpdated], [t].[ProfileName], [t].[ProfilePictureUrl], [t].[ProfileText], [t].[SiteId], [t].[VisitorsCount], [i].[Id], [i].[Name], [i].[ProfileId], [p0].[Id], [p0].[Description], [p0].[Name], [p0].[ProfileId], [n].[Id], [n].[Name], [n].[NetworkId], [n].[ProfileId], [p1].[Id], [p1].[Name], [p1].[ProfileId]
FROM (
    SELECT TOP(2) [p].[Id], [p].[Age], [p].[City], [p].[Country], [p].[County], [p].[DeactivatedAccount], [p].[Gender], [p].[HasPictures], [p].[LastLogin], [p].[MemberSince], [p].[PresentationUpdated], [p].[ProfileName], [p].[ProfilePictureUrl], [p].[ProfileText], [p].[SiteId], [p].[VisitorsCount]
    FROM [Profiles] AS [p]
    WHERE ([p].[SiteId] = '123') AND '123' IS NOT NULL
) AS [t]
LEFT JOIN [Interests] AS [i] ON [t].[Id] = [i].[ProfileId]
LEFT JOIN [Pets] AS [p0] ON [t].[Id] = [p0].[ProfileId]
LEFT JOIN [Networks] AS [n] ON [t].[Id] = [n].[ProfileId]
LEFT JOIN [PersonalityTraits] AS [p1] ON [t].[Id] = [p1].[ProfileId]
ORDER BY [t].[Id], [i].[Id], [p0].[Id], [n].[Id], [p1].[Id]

Iпопытался запустить реальный SQL в SSMS и завершился со следующей ошибкой:

Сообщение 1105, уровень 17, состояние 2, строка 1
Не удалось выделить место для объекта 'dbo.SORT временнозапустите хранилище: 140737692565504 'в базе данных' tempdb ', потому что файловая группа' PRIMARY 'заполнена. Создайте место на диске, удалив ненужные файлы, удалив объекты в файловой группе, добавив дополнительные файлы в файловую группу или установив автоматический рост для существующих файлов в файловой группе.

Мой tempdb теперь полностью заполнендиск базы данных. Я пробовал 10 других идентификаторов, и тот же запрос выполняется мгновенно.

Я попытался снова сжать tempdb с помощью команды DBCC SHRINKDATABASE(tempdb, 10);, и она работала нормально. Однако, когда я снова попытался выполнить запросы, произошло то же самое. Если я пропущу, включая таблицы, все работает нормально. В чем может быть проблема здесь и как я могу это исправить? Это известная ошибка в EF Core 3.0? Глядя на запрос в EF Core 2.2.6, он выполняет отдельные выборки, подобные этим для всех таблиц:

SELECT [x.Interests].[Id], [x.Interests].[Name], [x.Interests].[ProfileId]
FROM [Interests] AS [x.Interests]
INNER JOIN (
    SELECT TOP(1) [x0].[Id]
    FROM [Profiles] AS [x0]
    WHERE [x0].[SiteId] = '123'
    ORDER BY [x0].[Id]
) AS [t] ON [x.Interests].[ProfileId] = [t].[Id]
ORDER BY [t].[Id]

Ответы [ 2 ]

1 голос
/ 12 октября 2019

Это документированное критическое изменение в EF Core 3: Стремительная загрузка связанных сущностей теперь происходит в одном запросе

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

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

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

0 голосов
/ 14 октября 2019

Дополнение к ответу @ DavidBrowne-Microsoft.

Предположим, у нас есть следующие объекты и навигации в модели

Customer
Customer.Address (reference nav)
Customer.Orders (collection nav)
Order.OrderDetails (collection nav)
Order.OrderDiscount (reference nav)

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

var baseQuery = db.Customers.Include(c => c.Address).Where(c => c.CustomerName == "John");
var result = baseQuery.ToList(); // Or async method, If doing FirstOrDefault, add Take(1) to base query
baseQuery.Include(c => c.Orders).ThenInclude(o => o.OrderDiscount).SelectMany(c => c.Orders).Load();
baseQuery.SelectMany(c => c.Orders).SelectMany(o => o.OrderDetails).Load();

Это сгенерирует 3 запроса к серверу. Это будет немного более оптимизированный SQL, чем то, что генерирует EF Core 2.2. А StateManager исправит навигацию. Это также позволяет избежать дублирования любых записей, поступающих с сервера на клиент.

Сгенерированный SQL:

// Customer Include Address
SELECT [c].[Id], [c].[CustomerName], [a].[Id], [a].[City], [a].[CustomerId]
FROM [Customers] AS [c]
LEFT JOIN [Address] AS [a] ON [c].[Id] = [a].[CustomerId]
WHERE ([c].[CustomerName] = N'John') AND [c].[CustomerName] IS NOT NULL

// Order Include Order discount
SELECT [o].[Id], [o].[CustomerId], [o].[OrderDate], [o0].[Id], [o0].[Discount], [o0].[OrderId]
FROM [Customers] AS [c]
INNER JOIN [Order] AS [o] ON [c].[Id] = [o].[CustomerId]
LEFT JOIN [OrderDiscount] AS [o0] ON [o].[Id] = [o0].[OrderId]
WHERE ([c].[CustomerName] = N'John') AND [c].[CustomerName] IS NOT NULL

// OrderDetails
SELECT [o0].[Id], [o0].[OrderId], [o0].[ProductName]
FROM [Customers] AS [c]
INNER JOIN [Order] AS [o] ON [c].[Id] = [o].[CustomerId]
INNER JOIN [OrderDetail] AS [o0] ON [o].[Id] = [o0].[OrderId]
WHERE ([c].[CustomerName] = N'John') AND [c].[CustomerName] IS NOT NULL

Редактировать: если у вас нет навигации CLR для использования в SelectMany, вы можете использовать EF.Propertyдля ссылки на коллекцию навигации.

Источник: https://github.com/aspnet/EntityFrameworkCore/issues/18022#issuecomment-537219137

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...