Start Debugging

O novo analisador EF1004 do EF Core 11 detecta um erro assíncrono silencioso

O EF Core 11 Preview 5 traz o analisador EF1004. Ele sinaliza ToAsyncEnumerable() em um IQueryable para que você não enumere uma consulta de banco de dados de forma síncrona dentro de um await foreach.

O .NET 11 Preview 5, lançado em 2026-06-09, adiciona um novo analisador do EF Core com o identificador de diagnóstico EF1004. Ele detecta um erro que ficou muito mais fácil de cometer desde que System.Linq.AsyncEnumerable passou a fazer parte da BCL no .NET 10: chamar ToAsyncEnumerable() em uma consulta do EF Core e executá-la de forma síncrona sem perceber.

Como a chamada errada se infiltrou

Agora que System.Linq.AsyncEnumerable é distribuído no runtime, seus métodos de extensão estão disponíveis em quase todo lugar. Um deles é ToAsyncEnumerable(), que adapta qualquer IEnumerable<T> para um IAsyncEnumerable<T>. Um IQueryable<T> do EF Core também é um IEnumerable<T>, então a chamada compila sem erros e parece correta ao lado de um await foreach:

// Looks async. Is not.
await foreach (var blog in db.Blogs.ToAsyncEnumerable())
{
    Console.WriteLine(blog.Name);
}

O problema é que ToAsyncEnumerable() encapsula uma enumeração síncrona. Ele percorre o IQueryable usando o enumerador bloqueante, então a ida e volta ao banco de dados é executada na thread que fez a chamada. O await foreach te dá a sintaxe do streaming sem nada do comportamento assíncrono. Sob carga, essa é exatamente a forma que esgota o pool de threads e produz deadlocks que só aparecem em produção.

O que o EF1004 espera em vez disso

O EF Core expõe seu próprio método AsAsyncEnumerable(). Esse sim roteia a consulta pelo pipeline assíncrono do EF Core, de modo que cada linha é materializada conforme sai do DbDataReader sem bloquear uma thread:

// Runs through EF Core's async query pipeline.
await foreach (var blog in db.Blogs.AsAsyncEnumerable())
{
    Console.WriteLine(blog.Name);
}

Os dois nomes de método diferem por três caracteres, ambos retornam IAsyncEnumerable<T> e ambos compilam. Antes do Preview 5, nada te dizia qual deles você tinha escolhido. O EF1004 dispara em tempo de compilação no momento em que você chama ToAsyncEnumerable() em um IQueryable<T>, apontando você para AsAsyncEnumerable().

Transformando isso em erro

O analisador é distribuído no pacote de analisadores do EF Core e roda durante uma compilação normal, então você recebe o aviso sem configuração extra em um projeto que referencia Microsoft.EntityFrameworkCore 11.0.0. Se você quer garantir que ninguém publique a versão síncrona, promova-o no seu arquivo de projeto:

<PropertyGroup>
  <WarningsAsErrors>$(WarningsAsErrors);EF1004</WarningsAsErrors>
</PropertyGroup>

Isso combina naturalmente com os padrões de streaming abordados em Como usar IAsyncEnumerable<T> com EF Core 11: AsAsyncEnumerable() é a chamada que faz o await foreach realmente transmitir. O EF1004 é apenas a proteção que impede o método parecido de passar despercebido na revisão de código.

Fonte: as notas de versão do EF Core para o .NET 11 Preview 5 e o anúncio do .NET 11 Preview 5.

Comments

Sign in with GitHub to comment. Reactions and replies thread back to the comments repo.

< Voltar