Большое время выполнения простого запроса с Entity Framework - PullRequest
0 голосов
/ 23 января 2019

У меня странное поведение из Entity Framework 6. У меня простой (простой, где и простой выбор) запрос, который занимает 30 с.

Я использовал Sql Profiler, чтобы посмотреть, какой SQL-код выполняется. Я использую метод Where, а затем метод FirstOrDefault, чтобы получить элемент. Затем я попробовал другой запрос, я сделал ToList (для извлечения данных), затем FirstOrDefault, и это занимает менее 1 секунды.

Original code (takes 30s to be executed):
-----------------------------------------

id = Container.SocialNetworks.Where(a => a.SocialNetwork == EnumSocialNetwork.LinkedIn && a.Link == linkedinurl && a.User.TenantID == Container.TenantId).Select(i => i.UserID).FirstOrDefault();

From SQL Profiler :
-------------------

exec sp_executesql N'SELECT 
    [Limit1].[UserID] AS [UserID]
    FROM ( SELECT TOP (1) 
        [Extent1].[UserID] AS [UserID]
        FROM  [dbo].[SocialNetworks] AS [Extent1]
        INNER JOIN [dbo].[Users] AS [Extent2] ON [Extent1].[UserID] = [Extent2].[ID]
        WHERE (0 = [Extent1].[SocialNetwork]) AND (([Extent1].[Link] = @p__linq__0) OR (([Extent1].[Link] IS NULL) AND (@p__linq__0 IS NULL))) AND ([Extent2].[TenantID] = @p__linq__1)
    )  AS [Limit1]',N'@p__linq__0 nvarchar(4000),@p__linq__1 int',@p__linq__0=N'linkedin.com/in/a-profile',@p__linq__1=5

After testing another solutions (takes less than 1s):
-----------------------------------------------------

id = Container.SocialNetworks.Where(a => a.SocialNetwork == EnumSocialNetwork.LinkedIn && a.Link == linkedinurl && a.User.TenantID == Container.TenantId).Select(i => i.UserID).ToList().FirstOrDefault();

From SQL Profiler:
------------------

exec sp_executesql N'SELECT 
    [Extent1].[UserID] AS [UserID]
    FROM  [dbo].[SocialNetworks] AS [Extent1]
    INNER JOIN [dbo].[Users] AS [Extent2] ON [Extent1].[UserID] = [Extent2].[ID]
    WHERE (0 = [Extent1].[SocialNetwork]) AND (([Extent1].[Link] = @p__linq__0) OR (([Extent1].[Link] IS NULL) AND (@p__linq__0 IS NULL))) AND ([Extent2].[TenantID] = @p__linq__1)',N'@p__linq__0 nvarchar(4000),@p__linq__1 int',@p__linq__0=N'linkedin.com/in/a-profile-as',@p__linq__1=5

Как видите, я использую ToList для извлечения данных перед фильтрацией с помощью FirstOrDefault. И, как правило, не рекомендуется делать ToList, энергичная загрузка. Почему Entity Framework помещает выбор в выборку, когда я использую FirstOrDefault?

Я прошу прощения за мой английский и надеюсь, что правильно объяснил мою проблему.

РЕДАКТИРОВАТЬ:

У меня есть кое-что интересное, чтобы добавить, когда значение «linkedinurl» не существует, и только когда оно не существует, в базе данных оба запроса занимают менее 1 секунды.

EDIT 2:

После написания комментария я хотел бы добавить, что наша база данных находится на Azure. И проблема не появляется в простой базе данных SQLEXPRESS. Более того, эта проблема появилась как 4 или 5 дней назад.

1 Ответ

0 голосов
/ 23 января 2019

Это потому, что вы используете FirstOrDefault ПОСЛЕ комбинации where().Select().

Первый запрос будет работать лучше так:

id = Container.SocialNetworks.FirstOrDefault(a => a.SocialNetwork == EnumSocialNetwork.LinkedIn && a.Link == linkedinurl && a.User.TenantID == Container.TenantId)?.UserID;

Как видите, я использую FirstOrDefault так же, как вы использовали Where, но это загрузит весь объект, как описано в комментариях.

Почему ваш второй запрос быстрее? Поскольку вы завершили запрос с помощью ToList(), поэтому часть FirstOrDefault применяется только в вашем коде c #, ПОСЛЕ того, как загружены строки, а не в БД с двойным выбором.

Редактировать:

Попытка этих двух строк лучше выделить основную причину:

1. Попробуйте заказать ваш набор:

id = Container.SocialNetworks
   .Where(a => a.SocialNetwork == EnumSocialNetwork.LinkedIn && a.Link == linkedinurl && a.User.TenantID == Container.TenantId)
   .OrderBy(t => t.UserID).Select(i => i.UserID).FirstOrDefault();

2. Используйте агрегатную функцию:

id = Container.SocialNetworks
    .Where(a => a.SocialNetwork == EnumSocialNetwork.LinkedIn && a.Link == linkedinurl && a.User.TenantID == Container.TenantId)
    .Min(i => i.UserID);
...