O problema era um plano de consulta obsoleto ou incorreto para minha consulta.
Resolvi o problema de excluir os planos de consulta existentes para esta consulta.
Obrigado a Vladimir Baranov por me indicar sommarskog.se/query-plan-mysteries.html. Obrigado também a tschmit007 e annemartijn.
Eu tive que identificar os planos de consulta para minha consulta no banco de dados usando a seguinte consulta:
SELECT qs.plan_handle, a.attrlist, est.dbid, text
FROM sys.dm_exec_query_stats qs
CROSS APPLY sys.dm_exec_sql_text(qs.sql_handle) est
CROSS APPLY (SELECT epa.attribute + '=' + convert(nvarchar(127), epa.value) + ' '
FROM sys.dm_exec_plan_attributes(qs.plan_handle) epa
WHERE epa.is_cache_key = 1
ORDER BY epa.attribute
FOR XML PATH('')) AS a(attrlist)
WHERE est.text LIKE '%standardHourRate%' and est.text like '%q__7%'and est.text like '%Unit Overhead%'
AND est.text NOT LIKE '%sys.dm_exec_plan_attributes%'
Esta é uma versão levemente modificada da consulta do artigo de sommarskog. Observe que você precisa colocar seu próprio código nas instruções like para encontrar sua consulta. Essa consulta responde com a lista de atributos e o identificador de plano para cada plano de consulta para minha consulta.
Tentei descobrir qual plano veio do SSMS e qual do EF, então deletei todos eles, usando a seguinte sintaxe:
dbcc freeproccache([your plan handle here])
O novo plano criado para minha consulta EF funcionou perfeitamente. Aparentemente, o plano EF não levou em consideração que eu tinha atualizado as estatísticas no banco de dados recentemente. Infelizmente, não sei como fazer um sp_recompile para uma consulta EF.