Оптимизация вызова LINQ Any () в Entity Framework - PullRequest
8 голосов
/ 18 января 2011

После профилирования уровня базы данных на основе Entity Framework 4.0 я обнаружил, что основным фактором, влияющим на производительность, является простая функция LINQ Any (), которую я использую, чтобы проверить, существует ли уже сущность в базе данных. Проверка Any () выполняется на несколько порядков медленнее, чем сохранение объекта. В базе данных относительно мало строк, и проверяемые столбцы индексируются.

Я использую следующий LINQ для проверки существования группы настроек:

from sg in context.SettingGroups
where sg.Group.Equals(settingGroup) && sg.Category.Equals(settingCategory)
select sg).Any()

Это генерирует следующий SQL (кроме того, мой профилировщик SQL утверждает, что запрос выполняется дважды):

exec sp_executesql N'SELECT 
CASE WHEN ( EXISTS (SELECT 
    1 AS [C1]
    FROM [dbo].[SettingGroups] AS [Extent1]
    WHERE ([Extent1].[Group] = @p__linq__0) AND ([Extent1].[Category] = @p__linq__1)
)) THEN cast(1 as bit) WHEN ( NOT EXISTS (SELECT 
    1 AS [C1]
    FROM [dbo].[SettingGroups] AS [Extent2]
    WHERE ([Extent2].[Group] = @p__linq__0) AND ([Extent2].[Category] = @p__linq__1)
)) THEN cast(0 as bit) END AS [C1]
FROM  ( SELECT 1 AS X ) AS [SingleRowTable1]',N'@p__linq__0 nvarchar(4000),@p__linq__1 nvarchar(4000)',@p__linq__0=N'Cleanup',@p__linq__1=N'Mediator'

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

Есть ли способ заставить такую ​​проверку "Exist" работать быстрее с EF?

Я, вероятно, должен упомянуть, что я также использую самоконтрольные сущности в n-уровневой архитектуре. В некоторых сценариях состояние ChangeTracker для некоторых объектов установлено как «Добавлено», даже если они уже существуют в базе данных. Вот почему я использую проверку, чтобы соответствующим образом изменить состояние ChangeTracker, если обновление базы данных вызвало исключение ошибки вставки.

Ответы [ 4 ]

1 голос
/ 20 января 2011

Попробуйте добавить индекс в таблицу базы данных "SettingGroups" по группам и категориям.

Кстати, это приводит к тому же sql?

var ok = context.SettingGroups.Any(sg => sg.Group==settingGroup && sg.Category==settingCategory);
0 голосов
/ 27 июля 2012

Проблема в том, что Entity Framework (по крайней мере, EF4) генерирует глупый SQL.Следующий код, кажется, генерирует приличный SQL с минимальной болью.

public static class LinqExt 
{
    public static bool BetterAny<T>( this IQueryable<T> queryable, Expression<Func<T, bool>> predicate)
    {
        return queryable.Where(predicate).Select(x => (int?)1).FirstOrDefault().HasValue;
    }

    public static bool BetterAny<T>( this IQueryable<T> queryable)
    {
        return queryable.Select(x => (int?)1).FirstOrDefault().HasValue;
    }

}

Тогда вы можете сделать:

(from sg in context.SettingGroups
where sg.Group.Equals(settingGroup) && sg.Category.Equals(settingCategory)
select sg).BetterAny()

или даже:

context.SettingGroups.BetterAny(sg => sg.Group.Equals(settingGroup) && sg.Category.Equals(settingCategory));
0 голосов
/ 18 января 2011

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

Сечение

 SELECT 
 1 AS [C1]
 FROM [dbo].[SettingGroups] AS [Extent1]
 WHERE ([Extent1].[Group] = @p__linq__0) AND ([Extent1].[Category] = @p__linq__1)

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

0 голосов
/ 18 января 2011

Я знаю, это звучит жалкое решение, но что произойдет, если вы используете Count вместо Any?

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