В запросе Entity Framework отсутствует отфильтрованный индекс на SQL Server 2012

#c# #sql-server #entity-framework #prepared-statement #filtered-index

#c# #sql-сервер #entity-framework #подготовленный оператор #filtered-index

Вопрос:

У меня есть этот запрос EF: (сохранена только основная часть)

 int maxRetryCount = 5;
var erroredArchiveFilesQuery =
  transitionLogSessionContext.Set<ArchivedFile>().Where(f => 
  f.RetryCount < maxRetryCount
).Take(maxBatchSize);
  

В нем отсутствует отфильтрованный доступный индекс.

Тогда как при удалении переменной maxRetryCount как таковой

 var erroredArchiveFilesQuery =
transitionLogSessionContext.Set<ArchivedFile>().Where(f => 
f.RetryCount < 5 amp;amp; 
).Take(maxBatchSize);
  

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

Фактический SQL из первого запроса EF…

 SELECT TOP (500) 
    [Extent1].[Id] AS [Id], 
     ..
FROM  
    [ArchivedFile] AS [Extent1]
WHERE 
    ([Extent1].[RetryCount] < @p__linq__0 ) 
  

Отфильтрованный индекс содержит столбец RetryCount и фильтр ‘retryCount < 5’

Как я могу выполнить запрос ef с переменной, которая попадет в отфильтрованный индекс?

Я предполагаю, что проблема заключается в том, что оператор EF готовится, чтобы его можно было использовать повторно, и это сбивает с толку SQL Server.

Комментарии:

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

2. У меня были проблемы с отображением среднего раздела кода в виде кода, но его переписывание помогло. Я не собирался компилировать код, но, надеюсь, все необходимые части есть. Если проблема сейчас не имеет смысла, просто дайте мне знать, где я должен очистить. Спасибо

3. .Где(f => f.retryCount < 5 amp;amp; ) Что должен делать этот фрагмент кода?

4. Пожалуйста, не могли бы вы показать определение таблицы и отфильтрованного индекса

5. Я подозреваю, что это связано с тем, что ваш queryplan генерируется для всех случаев @p_linq_0, который может иметь любое значение, поэтому план, использующий отфильтрованный индекс, не всегда будет действительным.

Ответ №1:

Необходимо убедиться, что SQL Server перекомпилирует план каждый раз на основе фактического значения параметра maxRetryCount . Это непросто в EF, но можно сделать с помощью пользовательского перехватчика базы данных, чтобы добавить option (recompile) подсказку к вашему запросу.

Подробности смотрите здесь в статье SimpleTalk

 public class RecompileDbCommandInterceptor : IDbCommandInterceptor
{
    public void ReaderExecuting(DbCommand command,  DbCommandInterceptionContext<DbDataReader> interceptionContext)
    {
        if(!command.CommandText.EndsWith(" option(recompile)"))
        {
            command.CommandText  =  " option(recompile)";
        }
    }
}
  

Вы можете использовать его следующим образом:

 var interceptor = new RecompileDbCommandInterceptor();
DbInterception.Add(interceptor);

int maxRetryCount = 5;
var erroredArchiveFilesQuery =
  transitionLogSessionContext.Set<ArchivedFile>().Where(f => 
  f.RetryCount < maxRetryCount
).Take(maxBatchSize);

DbInterception.Remove(interceptor);
  

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