#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);
Обратите внимание, что этот перехват включен глобально, а не для конкретного экземпляра контекста, поэтому вы, вероятно, захотите отключить его снова, чтобы не затрагивать другие запросы.