Entity Framework 查询缺少 SQL Server 2012 上的过滤索引

Entity Framework query missing a filtered index on SQL Server 2012

我有这个 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 && 
).Take(maxBatchSize);

将使用过滤后的索引。

来自第一个 EF 查询的实际 SQL...

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

过滤后的索引包含列 RetryCount 和过滤器 'RetryCount < 5'

如何使用将命中过滤索引的变量进行 ef 查询?

我假设问题出在正在准备的 EF 语句中,因此它可以被重用,这会使 SQL 服务器感到困惑。

您需要确保SQL服务器每次根据参数maxRetryCount的实际值重新编译计划。这在 EF 中并不容易,但可以使用自定义数据库拦截器将 option (recompile) 提示添加到您的查询中来完成。

在此处查看详细信息SimpleTalk Article

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);

请注意,此拦截是全局启用的,而不是针对上下文的特定实例,因此您可能希望再次禁用它,以免其他查询受到影响。