Práticas recomendadas de desempenho de ASP.NET CoreASP.NET Core Performance Best Practices

Por Mike RousosBy Mike Rousos

Este artigo fornece diretrizes para práticas recomendadas de desempenho com o ASP.NET Core.This article provides guidelines for performance best practices with ASP.NET Core.

Cache agressivamenteCache aggressively

O Caching é discutido em várias partes deste documento.Caching is discussed in several parts of this document. Para obter mais informações, consulte Cache de resposta no ASP.NET Core.For more information, see Cache de resposta no ASP.NET Core.

Entender os caminhos de código quenteUnderstand hot code paths

Neste documento, um caminho de código quente é definido como um caminho de código que é chamado com frequência e onde ocorre grande parte do tempo de execução.In this document, a hot code path is defined as a code path that is frequently called and where much of the execution time occurs. Os caminhos de código quente normalmente limitam o desempenho e a expansão do aplicativo e são discutidos em várias partes deste documento.Hot code paths typically limit app scale-out and performance and are discussed in several parts of this document.

Evite chamadas de bloqueioAvoid blocking calls

ASP.NET Core aplicativos devem ser criados para processar várias solicitações simultaneamente.ASP.NET Core apps should be designed to process many requests simultaneously. As APIs assíncronas permitem que um pequeno pool de threads lide com milhares de solicitações simultâneas, não aguardando chamadas de bloqueio.Asynchronous APIs allow a small pool of threads to handle thousands of concurrent requests by not waiting on blocking calls. Em vez de aguardar a conclusão de uma tarefa síncrona de execução longa, o thread pode trabalhar em outra solicitação.Rather than waiting on a long-running synchronous task to complete, the thread can work on another request.

Um problema de desempenho comum no ASP.NET Core aplicativos é bloquear chamadas que poderiam ser assíncronas.A common performance problem in ASP.NET Core apps is blocking calls that could be asynchronous. Muitas chamadas de bloqueio síncronos levam à privação do pool de threads e tempos de resposta degradados.Many synchronous blocking calls lead to Thread Pool starvation and degraded response times.

Não:Do not:

  • Bloquear a execução assíncrona chamando Task. Wait ou Task. Result.Block asynchronous execution by calling Task.Wait or Task.Result.
  • Adquirir bloqueios em caminhos de código comuns.Acquire locks in common code paths. Os aplicativos ASP.NET Core são mais com o melhor desempenho quando arquitetados para executar código em paralelo.ASP.NET Core apps are most performant when architected to run code in parallel.

Fazer:Do:

  • Torne os caminhos de código dinâmico assíncronos.Make hot code paths asynchronous.
  • Chame o acesso a dados e APIs de operações de longa execução de forma assíncrona.Call data access and long-running operations APIs asynchronously.
  • Torne as ações de página do controlador/Razor assíncronas.Make controller/Razor Page actions asynchronous. A pilha de chamadas inteira é assíncrona para se beneficiar de padrões Async/Await .The entire call stack is asynchronous in order to benefit from async/await patterns.

Um criador de perfil, como Perfview, pode ser usado para localizar threads frequentemente adicionados ao pool de threads.A profiler, such as PerfView, can be used to find threads frequently added to the Thread Pool. O evento Microsoft-Windows-DotNETRuntime/ThreadPoolWorkerThread/Start indica um thread adicionado ao pool de threads.The Microsoft-Windows-DotNETRuntime/ThreadPoolWorkerThread/Start event indicates a thread added to the thread pool.

Minimizar alocações de objeto grandeMinimize large object allocations

O coletor de lixo do .NET Core gerencia a alocação e a liberação da memória automaticamente em aplicativos ASP.NET Core.The .NET Core garbage collector manages allocation and release of memory automatically in ASP.NET Core apps. A coleta de lixo automática geralmente significa que os desenvolvedores não precisam se preocupar com o modo como ou quando a memória é liberada.Automatic garbage collection generally means that developers don't need to worry about how or when memory is freed. No entanto, a limpeza de objetos não referenciados leva tempo de CPU, de modo que os desenvolvedores devem minimizar a alocação de objetos em caminhos de código quente.However, cleaning up unreferenced objects takes CPU time, so developers should minimize allocating objects in hot code paths. A coleta de lixo é especialmente cara em objetos grandes (> 85 K bytes).Garbage collection is especially expensive on large objects (> 85 K bytes). Os objetos grandes são armazenados na heap de objeto grande e exigem uma coleta de lixo completa (geração 2) para limpeza.Large objects are stored on the large object heap and require a full (generation 2) garbage collection to clean up. Diferentemente das coleções de geração 0 e geração 1, uma coleção de geração 2 requer uma suspensão temporária da execução do aplicativo.Unlike generation 0 and generation 1 collections, a generation 2 collection requires a temporary suspension of app execution. A alocação e a desalocação frequentes de objetos grandes podem causar um desempenho inconsistente.Frequent allocation and de-allocation of large objects can cause inconsistent performance.

RecommendationsRecommendations:

  • Considere armazenar em cache objetos grandes que são usados com frequência.Do consider caching large objects that are frequently used. O Caching de objetos grandes impede alocações caras.Caching large objects prevents expensive allocations.
  • Faça buffers de pool usando um ArrayPool<T> para armazenar matrizes grandes.Do pool buffers by using an ArrayPool<T> to store large arrays.
  • Não aloque muitos objetos grandes de curta duração em caminhos de código quente.Do not allocate many, short-lived large objects on hot code paths.

Problemas de memória, como os anteriores, podem ser diagnosticados examinando as estatísticas de GC (coleta de lixo) em Perfview e examinando:Memory issues, such as the preceding, can be diagnosed by reviewing garbage collection (GC) stats in PerfView and examining:

  • Tempo de pausa da coleta de lixo.Garbage collection pause time.
  • Qual percentual do tempo do processador é gasto na coleta de lixo.What percentage of the processor time is spent in garbage collection.
  • Quantas coletas de lixo são de geração 0, 1 e 2.How many garbage collections are generation 0, 1, and 2.

Para obter mais informações, consulte coleta de lixo e desempenho.For more information, see Garbage Collection and Performance.

Otimizar o acesso a dadosOptimize Data Access

As interações com um armazenamento de dados e outros serviços remotos geralmente são as partes mais lentas de um aplicativo ASP.NET Core.Interactions with a data store and other remote services are often the slowest parts of an ASP.NET Core app. Ler e gravar dados com eficiência é essencial para um bom desempenho.Reading and writing data efficiently is critical for good performance.

RecommendationsRecommendations:

  • Chame todas as APIs de acesso a dados de forma assíncrona.Do call all data access APIs asynchronously.
  • Não recupere mais dados do que o necessário.Do not retrieve more data than is necessary. Escreva consultas para retornar apenas os dados necessários para a solicitação HTTP atual.Write queries to return just the data that's necessary for the current HTTP request.
  • Considere o armazenamento em cache de dados acessados com frequência recuperados de um banco de dados ou serviço remoto se forem aceitáveis insuficientes.Do consider caching frequently accessed data retrieved from a database or remote service if slightly out-of-date data is acceptable. Dependendo do cenário, use um MemoryCache ou um DistributedCache.Depending on the scenario, use a MemoryCache or a DistributedCache. Para obter mais informações, consulte Cache de resposta no ASP.NET Core.For more information, see Cache de resposta no ASP.NET Core.
  • Minimize viagens de ida e volta da rede.Do minimize network round trips. O objetivo é recuperar os dados necessários em uma única chamada em vez de várias chamadas.The goal is to retrieve the required data in a single call rather than several calls.
  • Use consultas sem controle no Entity Framework Core ao acessar dados para fins somente leitura.Do use no-tracking queries in Entity Framework Core when accessing data for read-only purposes. EF Core pode retornar os resultados de consultas sem controle com mais eficiência.EF Core can return the results of no-tracking queries more efficiently.
  • Filtre e AGREGUE consultas LINQ (com instruções .Where, .Selectou .Sum, por exemplo) para que a filtragem seja executada pelo banco de dados.Do filter and aggregate LINQ queries (with .Where, .Select, or .Sum statements, for example) so that the filtering is performed by the database.
  • Considere que EF Core resolve alguns operadores de consulta no cliente, o que pode levar a uma execução de consulta ineficiente.Do consider that EF Core resolves some query operators on the client, which may lead to inefficient query execution. Para obter mais informações, consulte problemas de desempenho de avaliação do cliente.For more information, see Client evaluation performance issues.
  • Não use consultas de projeção em coleções, o que pode resultar na execução de consultas SQL "N + 1".Do not use projection queries on collections, which can result in executing "N + 1" SQL queries. Para obter mais informações, consulte otimização de Subconsultas correlacionadas.For more information, see Optimization of correlated subqueries.

Consulte alto desempenho do EF para obter abordagens que podem melhorar o desempenho em aplicativos de grande escala:See EF High Performance for approaches that may improve performance in high-scale apps:

É recomendável medir o impacto das abordagens de alto desempenho anteriores antes de confirmar a base de código.We recommend measuring the impact of the preceding high-performance approaches before committing the code base. A complexidade adicional das consultas compiladas pode não justificar a melhoria do desempenho.The additional complexity of compiled queries may not justify the performance improvement.

Problemas de consulta podem ser detectados examinando o tempo gasto acessando dados com Application insights ou com ferramentas de criação de perfil.Query issues can be detected by reviewing the time spent accessing data with Application Insights or with profiling tools. A maioria dos bancos de dados também disponibiliza estatísticas sobre consultas executadas com frequência.Most databases also make statistics available concerning frequently executed queries.

Conexões HTTP do pool com HttpClientFactoryPool HTTP connections with HttpClientFactory

Embora o HttpClient implemente a interface IDisposable, ele foi projetado para reutilização.Although HttpClient implements the IDisposable interface, it's designed for reuse. As instâncias de HttpClient fechadas deixam os soquetes abertos no estado TIME_WAIT por um curto período de tempo.Closed HttpClient instances leave sockets open in the TIME_WAIT state for a short period of time. Se um caminho de código que cria e descarta objetos de HttpClient é usado com frequência, o aplicativo pode esgotar os soquetes disponíveis.If a code path that creates and disposes of HttpClient objects is frequently used, the app may exhaust available sockets. O HttpClientFactory foi introduzido no ASP.NET Core 2,1 como uma solução para esse problema.HttpClientFactory was introduced in ASP.NET Core 2.1 as a solution to this problem. Ele lida com o pool de conexões HTTP para otimizar o desempenho e a confiabilidade.It handles pooling HTTP connections to optimize performance and reliability.

RecommendationsRecommendations:

Mantenha os caminhos de código comuns rápidosKeep common code paths fast

Você quer que todo o seu código seja rápido, freqüentemente chamados de caminhos de código são os mais importantes para otimizar:You want all of your code to be fast, frequently called code paths are the most critical to optimize:

  • Componentes de middleware no pipeline de processamento de solicitação do aplicativo, especialmente o middleware é executado no início do pipeline.Middleware components in the app's request processing pipeline, especially middleware run early in the pipeline. Esses componentes têm um grande impacto no desempenho.These components have a large impact on performance.
  • Código que é executado para cada solicitação ou várias vezes por solicitação.Code that's executed for every request or multiple times per request. Por exemplo, log personalizado, manipuladores de autorização ou inicialização de serviços transitórios.For example, custom logging, authorization handlers, or initialization of transient services.

RecommendationsRecommendations:

Concluir tarefas de execução longa fora de solicitações HTTPComplete long-running Tasks outside of HTTP requests

A maioria das solicitações para um aplicativo ASP.NET Core pode ser tratada por um controlador ou modelo de página chamando os serviços necessários e retornando uma resposta HTTP.Most requests to an ASP.NET Core app can be handled by a controller or page model calling necessary services and returning an HTTP response. Para algumas solicitações que envolvem tarefas de longa execução, é melhor tornar o processo de solicitação/resposta assíncrono completo.For some requests that involve long-running tasks, it's better to make the entire request-response process asynchronous.

RecommendationsRecommendations:

  • Não aguarde a conclusão de tarefas de execução longa como parte do processamento de solicitação HTTP comum.Do not wait for long-running tasks to complete as part of ordinary HTTP request processing.
  • Considere manipular solicitações de execução longa com serviços em segundo plano ou fora do processo com uma função do Azure.Do consider handling long-running requests with background services or out of process with an Azure Function. Concluir o trabalho fora do processo é especialmente benéfico para tarefas com uso intensivo de CPU.Completing work out-of-process is especially beneficial for CPU-intensive tasks.
  • Use opções de comunicação em tempo real, como SignalR, para se comunicar com os clientes de forma assíncrona.Do use real-time communication options, such as SignalR, to communicate with clients asynchronously.

Ativos do cliente reduzirMinify client assets

ASP.NET Core aplicativos com front-ends complexos frequentemente atendem a muitos arquivos JavaScript, CSS ou de imagem.ASP.NET Core apps with complex front-ends frequently serve many JavaScript, CSS, or image files. O desempenho das solicitações de carregamento inicial pode ser melhorado pelo:Performance of initial load requests can be improved by:

  • Agrupamento, que combina vários arquivos em um.Bundling, which combines multiple files into one.
  • Minificar, que reduz o tamanho dos arquivos removendo espaços em branco e comentários.Minifying, which reduces the size of files by removing whitespace and comments.

RecommendationsRecommendations:

  • Use o suporte interno do ASP.NET Core para agrupar e minificar ativos do cliente.Do use ASP.NET Core's built-in support for bundling and minifying client assets.
  • Considere outras ferramentas de terceiros, como o webpack, para o gerenciamento de ativos de cliente complexo.Do consider other third-party tools, such as Webpack, for complex client asset management.

Compactar respostasCompress responses

Reduzir o tamanho da resposta geralmente aumenta a capacidade de resposta de um aplicativo, muitas vezes drasticamente.Reducing the size of the response usually increases the responsiveness of an app, often dramatically. Uma maneira de reduzir os tamanhos de carga é compactar as respostas de um aplicativo.One way to reduce payload sizes is to compress an app's responses. Para obter mais informações, consulte compactação de resposta.For more information, see Response compression.

Usar a versão mais recente do ASP.NET CoreUse the latest ASP.NET Core release

Cada nova versão do ASP.NET Core inclui melhorias de desempenho.Each new release of ASP.NET Core includes performance improvements. Otimizações no .NET Core e ASP.NET Core significam que as versões mais recentes geralmente superam as versões mais antigas.Optimizations in .NET Core and ASP.NET Core mean that newer versions generally outperform older versions. Por exemplo, o .NET Core 2,1 adicionou suporte para expressões regulares compiladas e se beneficiou da Span<T>.For example, .NET Core 2.1 added support for compiled regular expressions and benefitted from Span<T>. ASP.NET Core 2,2 adicionado suporte para HTTP/2.ASP.NET Core 2.2 added support for HTTP/2. ASP.NET Core 3,0 adiciona muitas melhorias que reduzem o uso de memória e melhoram a taxa de transferência.ASP.NET Core 3.0 adds many improvements that reduce memory usage and improve throughput. Se o desempenho for uma prioridade, considere atualizar para a versão atual do ASP.NET Core.If performance is a priority, consider upgrading to the current version of ASP.NET Core.

Minimizar exceçõesMinimize exceptions

As exceções devem ser raras.Exceptions should be rare. Lançar e capturar exceções é lento em relação a outros padrões de fluxo de código.Throwing and catching exceptions is slow relative to other code flow patterns. Por isso, as exceções não devem ser usadas para controlar o fluxo normal do programa.Because of this, exceptions shouldn't be used to control normal program flow.

RecommendationsRecommendations:

  • Não use exceções de lançamento ou captura como meio de fluxo de programa normal, especialmente em caminhos de código quente.Do not use throwing or catching exceptions as a means of normal program flow, especially in hot code paths.
  • Inclua a lógica no aplicativo para detectar e manipular condições que poderiam causar uma exceção.Do include logic in the app to detect and handle conditions that would cause an exception.
  • Gerar ou capturar exceções para condições incomuns ou inesperadas.Do throw or catch exceptions for unusual or unexpected conditions.

As ferramentas de diagnóstico do aplicativo, como o Application Insights, podem ajudar a identificar exceções comuns em um aplicativo que pode afetar o desempenho.App diagnostic tools, such as Application Insights, can help to identify common exceptions in an app that may affect performance.

Desempenho e confiabilidadePerformance and reliability

As seções a seguir fornecem dicas de desempenho e problemas e soluções de confiabilidade conhecidas.The following sections provide performance tips and known reliability problems and solutions.

Evitar leitura ou gravação síncrona no corpo de HttpRequest/HttpResponseAvoid synchronous read or write on HttpRequest/HttpResponse body

Todas as e/s no ASP.NET Core é assíncrona.All IO in ASP.NET Core is asynchronous. Os servidores implementam a interface Stream, que tem sobrecargas síncronas e assíncronas.Servers implement the Stream interface, which has both synchronous and asynchronous overloads. Os itens assíncronos devem ser preferidos para evitar o bloqueio de threads do pool de threadsThe asynchronous ones should be preferred to avoid blocking thread pool threads. O bloqueio de threads pode levar à privação do pool de threads.Blocking threads can lead to thread pool starvation.

Não faça isso: O exemplo a seguir usa o ReadToEnd.Do not do this: The following example uses the ReadToEnd. Ele bloqueia o thread atual para aguardar o resultado.It blocks the current thread to wait for the result. Este é um exemplo de sincronização por Async.This is an example of sync over async.

public class BadStreamReaderController : Controller
{
    [HttpGet("/contoso")]
    public ActionResult<ContosoData> Get()
    {
        var json = new StreamReader(Request.Body).ReadToEnd();

        return JsonSerializer.Deserialize<ContosoData>(json);
    }
}

No código anterior, Get lê de forma síncrona todo o corpo da solicitação HTTP na memória.In the preceding code, Get synchronously reads the entire HTTP request body into memory. Se o cliente estiver carregando lentamente, o aplicativo estará fazendo a sincronização por meio de Async.If the client is slowly uploading, the app is doing sync over async. O aplicativo é sincronizado por Async porque Kestrel não oferece suporte a Leituras síncronas.The app does sync over async because Kestrel does NOT support synchronous reads.

Faça o seguinte: O exemplo a seguir usa ReadToEndAsync e não bloqueia o thread durante a leitura.Do this: The following example uses ReadToEndAsync and does not block the thread while reading.

public class GoodStreamReaderController : Controller
{
    [HttpGet("/contoso")]
    public async Task<ActionResult<ContosoData>> Get()
    {
        var json = await new StreamReader(Request.Body).ReadToEndAsync();

        return JsonSerializer.Deserialize<ContosoData>(json);
    }

}

O código anterior lê de forma assíncrona todo o corpo da solicitação HTTP na memória.The preceding code asynchronously reads the entire HTTP request body into memory.

Aviso

Se a solicitação for grande, a leitura do corpo de solicitação HTTP inteiro na memória poderá levar a uma condição de OOM (memória insuficiente).If the request is large, reading the entire HTTP request body into memory could lead to an out of memory (OOM) condition. OOM pode resultar em uma negação de serviço.OOM can result in a Denial Of Service. Para obter mais informações, consulte evitar a leitura de corpos de solicitação grandes ou corpos de resposta na memória deste documento.For more information, see Avoid reading large request bodies or response bodies into memory in this document.

Faça o seguinte: O exemplo a seguir é totalmente assíncrono usando um corpo de solicitação não armazenado em buffer:Do this: The following example is fully asynchronous using a non buffered request body:

public class GoodStreamReaderController : Controller
{
    [HttpGet("/contoso")]
    public async Task<ActionResult<ContosoData>> Get()
    {
        return await JsonSerializer.DeserializeAsync<ContosoData>(Request.Body);
    }
}

O código anterior desserializa de forma assíncrona o corpo da solicitação C# em um objeto.The preceding code asynchronously de-serializes the request body into a C# object.

Preferir ReadFormAsync sobre solicitação. FormPrefer ReadFormAsync over Request.Form

Use HttpContext.Request.ReadFormAsync em vez de HttpContext.Request.Form.Use HttpContext.Request.ReadFormAsync instead of HttpContext.Request.Form. HttpContext.Request.Form pode ser lido com segurança apenas com as seguintes condições:HttpContext.Request.Form can be safely read only with the following conditions:

  • O formulário foi lido por uma chamada para ReadFormAsynceThe form has been read by a call to ReadFormAsync, and
  • O valor de formulário armazenado em cache está sendo lido usando HttpContext.Request.FormThe cached form value is being read using HttpContext.Request.Form

Não faça isso: O exemplo a seguir usa HttpContext.Request.Form.Do not do this: The following example uses HttpContext.Request.Form. HttpContext.Request.Form usa a sincronização sobre Async e pode levar à privação do pool de threads.HttpContext.Request.Form uses sync over async and can lead to thread pool starvation.

public class BadReadController : Controller
{
    [HttpPost("/form-body")]
    public IActionResult Post()
    {
        var form =  HttpContext.Request.Form;

        Process(form["id"], form["name"]);

        return Accepted();
    }

Faça o seguinte: O exemplo a seguir usa HttpContext.Request.ReadFormAsync para ler o corpo do formulário de forma assíncrona.Do this: The following example uses HttpContext.Request.ReadFormAsync to read the form body asynchronously.

public class GoodReadController : Controller
{
    [HttpPost("/form-body")]
    public async Task<IActionResult> Post()
    {
       var form = await HttpContext.Request.ReadFormAsync();

        Process(form["id"], form["name"]);

        return Accepted();
    }

Evite ler corpos de solicitação grandes ou corpos de resposta na memóriaAvoid reading large request bodies or response bodies into memory

No .NET, cada alocação de objeto maior que 85 KB termina naLoh(Large Object heap).In .NET, every object allocation greater than 85 KB ends up in the large object heap (LOH). Os objetos grandes são caros de duas maneiras:Large objects are expensive in two ways:

  • O custo de alocação é alto porque a memória para um objeto grande alocado recentemente deve ser apagada.The allocation cost is high because the memory for a newly allocated large object has to be cleared. O CLR garante que a memória para todos os objetos alocados recentemente seja apagada.The CLR guarantees that memory for all newly allocated objects is cleared.
  • O LOH é coletado com o restante do heap.LOH is collected with the rest of the heap. O LOH requer uma coleta de lixo completa ou uma coleção de Gen2.LOH requires a full garbage collection or Gen2 collection.

Esta postagem de blog descreve o problema sucintamente:This blog post describes the problem succinctly:

Quando um objeto grande é alocado, ele é marcado como um objeto Gen 2.When a large object is allocated, it’s marked as Gen 2 object. Não Gen 0 como para objetos pequenos.Not Gen 0 as for small objects. As consequências são que, se você ficar sem memória no LOH, o GC limpará todo o heap gerenciado, não apenas o LOH.The consequences are that if you run out of memory in LOH, GC cleans up the whole managed heap, not only LOH. Portanto, ele limpa Gen 0, Gen 1 e Gen 2, incluindo LOH.So it cleans up Gen 0, Gen 1 and Gen 2 including LOH. Isso é chamado de coleta de lixo completa e é a coleta de lixo mais demorada.This is called full garbage collection and is the most time-consuming garbage collection. Para muitos aplicativos, pode ser aceitável.For many applications, it can be acceptable. Mas, definitivamente, não para servidores Web de alto desempenho, onde poucos buffers de memória Big são necessários para lidar com uma solicitação da Web média (leitura de um soquete, descompactar, decodificar JSON & mais).But definitely not for high-performance web servers, where few big memory buffers are needed to handle an average web request (read from a socket, decompress, decode JSON & more).

Naively armazenando um corpo de solicitação ou resposta grande em um único byte[] ou string:Naively storing a large request or response body into a single byte[] or string:

  • Pode resultar na execução rápida do espaço no LOH.May result in quickly running out of space in the LOH.
  • Pode causar problemas de desempenho para o aplicativo devido a todos os GCs em execução.May cause performance issues for the app because of full GCs running.

Trabalhando com uma API de processamento de dados síncronoWorking with a synchronous data processing API

Ao usar um serializador/desserializador que dá suporte apenas a leituras e gravações síncronas (por exemplo, JSON.net):When using a serializer/de-serializer that only supports synchronous reads and writes (for example, JSON.NET):

  • Armazenar em buffer os dados na memória de forma assíncrona antes de passá-los para o serializador/desserializador.Buffer the data into memory asynchronously before passing it into the serializer/de-serializer.

Aviso

Se a solicitação for grande, ela poderá levar a uma condição de OOM (memória insuficiente).If the request is large, it could lead to an out of memory (OOM) condition. OOM pode resultar em uma negação de serviço.OOM can result in a Denial Of Service. Para obter mais informações, consulte evitar a leitura de corpos de solicitação grandes ou corpos de resposta na memória deste documento.For more information, see Avoid reading large request bodies or response bodies into memory in this document.

ASP.NET Core 3,0 usa System.Text.Json por padrão para serialização JSON.ASP.NET Core 3.0 uses System.Text.Json by default for JSON serialization. System.Text.Json:System.Text.Json:

  • Lê e grava JSON de forma assíncrona.Reads and writes JSON asynchronously.
  • É otimizado para texto UTF-8.Is optimized for UTF-8 text.
  • Normalmente, maior desempenho do que Newtonsoft.Json.Typically higher performance than Newtonsoft.Json.

Não armazenar IHttpContextAccessor. HttpContext em um campoDo not store IHttpContextAccessor.HttpContext in a field

O IHttpContextAccessor. HttpContext retorna o HttpContext da solicitação ativa quando acessado a partir do thread de solicitação.The IHttpContextAccessor.HttpContext returns the HttpContext of the active request when accessed from the request thread. O IHttpContextAccessor.HttpContext não deve ser armazenado em um campo ou variável.The IHttpContextAccessor.HttpContext should not be stored in a field or variable.

Não faça isso: O exemplo a seguir armazena o HttpContext em um campo e tenta usá-lo mais tarde.Do not do this: The following example stores the HttpContext in a field, and then attempts to use it later.

public class MyBadType
{
    private readonly HttpContext _context;
    public MyBadType(IHttpContextAccessor accessor)
    {
        _context = accessor.HttpContext;
    }

    public void CheckAdmin()
    {
        if (!_context.User.IsInRole("admin"))
        {
            throw new UnauthorizedAccessException("The current user isn't an admin");
        }
    }
}

O código anterior freqüentemente captura um HttpContext nulo ou incorreto no construtor.The preceding code frequently captures a null or incorrect HttpContext in the constructor.

Faça o seguinte: O exemplo a seguir:Do this: The following example:

  • Armazena o IHttpContextAccessor em um campo.Stores the IHttpContextAccessor in a field.
  • Usa o campo HttpContext na hora correta e verifica null.Uses the HttpContext field at the correct time and checks for null.
public class MyGoodType
{
    private readonly IHttpContextAccessor _accessor;
    public MyGoodType(IHttpContextAccessor accessor)
    {
        _accessor = accessor;
    }

    public void CheckAdmin()
    {
        var context = _accessor.HttpContext;
        if (context != null && !context.User.IsInRole("admin"))
        {
            throw new UnauthorizedAccessException("The current user isn't an admin");
        }
    }
}

Não acessar o HttpContext de vários threadsDo not access HttpContext from multiple threads

HttpContext não é thread-safe.HttpContext is NOT thread-safe. O acesso a HttpContext de vários threads em paralelo pode resultar em um comportamento indefinido, como travamentos, falhas e corrupção de dados.Accessing HttpContext from multiple threads in parallel can result in undefined behavior such as hangs, crashes, and data corruption.

Não faça isso: O exemplo a seguir faz três solicitações paralelas e registra o caminho de solicitação de entrada antes e depois da solicitação HTTP de saída.Do not do this: The following example makes three parallel requests and logs the incoming request path before and after the outgoing HTTP request. O caminho da solicitação é acessado de vários threads, potencialmente em paralelo.The request path is accessed from multiple threads, potentially in parallel.

public class AsyncBadSearchController : Controller
{       
    [HttpGet("/search")]
    public async Task<SearchResults> Get(string query)
    {
        var query1 = SearchAsync(SearchEngine.Google, query);
        var query2 = SearchAsync(SearchEngine.Bing, query);
        var query3 = SearchAsync(SearchEngine.DuckDuckGo, query);

        await Task.WhenAll(query1, query2, query3);

        var results1 = await query1;
        var results2 = await query2;
        var results3 = await query3;

        return SearchResults.Combine(results1, results2, results3);
    }       

    private async Task<SearchResults> SearchAsync(SearchEngine engine, string query)
    {
        var searchResults = _searchService.Empty();
        try
        {
            _logger.LogInformation("Starting search query from {path}.", 
                                    HttpContext.Request.Path);
            searchResults = _searchService.Search(engine, query);
            _logger.LogInformation("Finishing search query from {path}.", 
                                    HttpContext.Request.Path);
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Failed query from {path}", 
                             HttpContext.Request.Path);
        }

        return await searchResults;
    }

Faça o seguinte: O exemplo a seguir copia todos os dados da solicitação de entrada antes de fazer as três solicitações paralelas.Do this: The following example copies all data from the incoming request before making the three parallel requests.

public class AsyncGoodSearchController : Controller
{       
    [HttpGet("/search")]
    public async Task<SearchResults> Get(string query)
    {
        string path = HttpContext.Request.Path;
        var query1 = SearchAsync(SearchEngine.Google, query,
                                 path);
        var query2 = SearchAsync(SearchEngine.Bing, query, path);
        var query3 = SearchAsync(SearchEngine.DuckDuckGo, query, path);

        await Task.WhenAll(query1, query2, query3);

        var results1 = await query1;
        var results2 = await query2;
        var results3 = await query3;

        return SearchResults.Combine(results1, results2, results3);
    }

    private async Task<SearchResults> SearchAsync(SearchEngine engine, string query,
                                                  string path)
    {
        var searchResults = _searchService.Empty();
        try
        {
            _logger.LogInformation("Starting search query from {path}.",
                                   path);
            searchResults = await _searchService.SearchAsync(engine, query);
            _logger.LogInformation("Finishing search query from {path}.", path);
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Failed query from {path}", path);
        }

        return await searchResults;
    }

Não use o HttpContext após a conclusão da solicitaçãoDo not use the HttpContext after the request is complete

HttpContext só será válida contanto que haja uma solicitação HTTP ativa no pipeline de ASP.NET Core.HttpContext is only valid as long as there is an active HTTP request in the ASP.NET Core pipeline. Todo o pipeline de ASP.NET Core é uma cadeia assíncrona de delegados que executa cada solicitação.The entire ASP.NET Core pipeline is an asynchronous chain of delegates that executes every request. Quando a Task retornada dessa cadeia for concluída, a HttpContext será reciclada.When the Task returned from this chain completes, the HttpContext is recycled.

Não faça isso: O exemplo a seguir usa async void que faz a solicitação HTTP ser concluída quando a primeira await é atingida:Do not do this: The following example uses async void which makes the HTTP request complete when the first await is reached:

  • Que é sempre uma prática inadequada em aplicativos ASP.NET Core.Which is ALWAYS a bad practice in ASP.NET Core apps.
  • Acessa o HttpResponse depois que a solicitação HTTP é concluída.Accesses the HttpResponse after the HTTP request is complete.
  • Falha no processo.Crashes the process.
public class AsyncBadVoidController : Controller
{
    [HttpGet("/async")]
    public async void Get()
    {
        await Task.Delay(1000);

        // The following line will crash the process because of writing after the 
        // response has completed on a background thread. Notice async void Get()

        await Response.WriteAsync("Hello World");
    }
}

Faça o seguinte: O exemplo a seguir retorna um Task à estrutura para que a solicitação HTTP não seja concluída até que a ação seja concluída.Do this: The following example returns a Task to the framework so the HTTP request doesn't complete until the action completes.

public class AsyncGoodTaskController : Controller
{
    [HttpGet("/async")]
    public async Task Get()
    {
        await Task.Delay(1000);

        await Response.WriteAsync("Hello World");
    }
}

Não Capture o HttpContext em threads em segundo planoDo not capture the HttpContext in background threads

Não faça isso: O exemplo a seguir mostra que um fechamento está capturando a HttpContext da propriedade Controller.Do not do this: The following example shows a closure is capturing the HttpContext from the Controller property. Esta é uma prática inadequada porque o item de trabalho pode:This is a bad practice because the work item could:

  • Executar fora do escopo da solicitação.Run outside of the request scope.
  • Tentativa de ler o HttpContexterrado.Attempt to read the wrong HttpContext.
[HttpGet("/fire-and-forget-1")]
public IActionResult BadFireAndForget()
{
    _ = Task.Run(async () =>
    {
        await Task.Delay(1000);

        var path = HttpContext.Request.Path;
        Log(path);
    });

    return Accepted();
}

Faça o seguinte: O exemplo a seguir:Do this: The following example:

  • Copia os dados necessários na tarefa em segundo plano durante a solicitação.Copies the data required in the background task during the request.
  • Não faz referência a nada do controlador.Doesn't reference anything from the controller.
[HttpGet("/fire-and-forget-3")]
public IActionResult GoodFireAndForget()
{
    string path = HttpContext.Request.Path;
    _ = Task.Run(async () =>
    {
        await Task.Delay(1000);

        Log(path);
    });

    return Accepted();
}

As tarefas em segundo plano devem ser implementadas como serviços hospedados.Background tasks should be implemented as hosted services. Saiba mais em Tarefas em segundo plano com serviços hospedados.For more information, see Background tasks with hosted services.

Não capture os serviços injetados nos controladores em threads em segundo planoDo not capture services injected into the controllers on background threads

Não faça isso: O exemplo a seguir mostra que um fechamento está capturando o DbContext do parâmetro de ação Controller.Do not do this: The following example shows a closure is capturing the DbContext from the Controller action parameter. Esta é uma prática inadequada.This is a bad practice. O item de trabalho pode ser executado fora do escopo da solicitação.The work item could run outside of the request scope. O ContosoDbContext tem como escopo a solicitação, resultando em um ObjectDisposedException.The ContosoDbContext is scoped to the request, resulting in an ObjectDisposedException.

[HttpGet("/fire-and-forget-1")]
public IActionResult FireAndForget1([FromServices]ContosoDbContext context)
{
    _ = Task.Run(async () =>
    {
        await Task.Delay(1000);

        context.Contoso.Add(new Contoso());
        await context.SaveChangesAsync();
    });

    return Accepted();
}

Faça o seguinte: O exemplo a seguir:Do this: The following example:

  • Injeta um IServiceScopeFactory para criar um escopo no item de trabalho em segundo plano.Injects an IServiceScopeFactory in order to create a scope in the background work item. IServiceScopeFactory é um singleton.IServiceScopeFactory is a singleton.
  • Cria um novo escopo de injeção de dependência no thread em segundo plano.Creates a new dependency injection scope in the background thread.
  • Não faz referência a nada do controlador.Doesn't reference anything from the controller.
  • Não captura o ContosoDbContext da solicitação de entrada.Doesn't capture the ContosoDbContext from the incoming request.
[HttpGet("/fire-and-forget-3")]
public IActionResult FireAndForget3([FromServices]IServiceScopeFactory 
                                    serviceScopeFactory)
{
    _ = Task.Run(async () =>
    {
        await Task.Delay(1000);

        using (var scope = serviceScopeFactory.CreateScope())
        {
            var context = scope.ServiceProvider.GetRequiredService<ContosoDbContext>();

            context.Contoso.Add(new Contoso());

            await context.SaveChangesAsync();                                        
        }
    });

    return Accepted();
}

O seguinte código realçado:The following highlighted code:

  • Cria um escopo para o tempo de vida da operação em segundo plano e resolve os serviços dele.Creates a scope for the lifetime of the background operation and resolves services from it.
  • Usa ContosoDbContext do escopo correto.Uses ContosoDbContext from the correct scope.
[HttpGet("/fire-and-forget-3")]
public IActionResult FireAndForget3([FromServices]IServiceScopeFactory 
                                    serviceScopeFactory)
{
    _ = Task.Run(async () =>
    {
        await Task.Delay(1000);

        using (var scope = serviceScopeFactory.CreateScope())
        {
            var context = scope.ServiceProvider.GetRequiredService<ContosoDbContext>();

            context.Contoso.Add(new Contoso());

            await context.SaveChangesAsync();                                        
        }
    });

    return Accepted();
}

Não modifique o código ou os cabeçalhos de status depois que o corpo da resposta for iniciadoDo not modify the status code or headers after the response body has started

ASP.NET Core não armazena em buffer o corpo da resposta HTTP.ASP.NET Core does not buffer the HTTP response body. Na primeira vez em que a resposta é gravada:The first time the response is written:

  • Os cabeçalhos são enviados junto com essa parte do corpo para o cliente.The headers are sent along with that chunk of the body to the client.
  • Não é mais possível alterar cabeçalhos de resposta.It's no longer possible to change response headers.

Não faça isso: O código a seguir tenta adicionar cabeçalhos de resposta depois que a resposta já tiver sido iniciada:Do not do this: The following code tries to add response headers after the response has already started:

app.Use(async (context, next) =>
{
    await next();

    context.Response.Headers["test"] = "test value";
});

No código anterior, context.Response.Headers["test"] = "test value"; gerará uma exceção se next() tiver sido gravado na resposta.In the preceding code, context.Response.Headers["test"] = "test value"; will throw an exception if next() has written to the response.

Faça o seguinte: O exemplo a seguir verifica se a resposta HTTP foi iniciada antes de modificar os cabeçalhos.Do this: The following example checks if the HTTP response has started before modifying the headers.

app.Use(async (context, next) =>
{
    await next();

    if (!context.Response.HasStarted)
    {
        context.Response.Headers["test"] = "test value";
    }
});

Faça o seguinte: O exemplo a seguir usa HttpResponse.OnStarting para definir os cabeçalhos antes que os cabeçalhos de resposta sejam liberados para o cliente.Do this: The following example uses HttpResponse.OnStarting to set the headers before the response headers are flushed to the client.

Verificar se a resposta não foi iniciada permite registrar um retorno de chamada que será invocado logo antes que os cabeçalhos de resposta sejam gravados.Checking if the response has not started allows registering a callback that will be invoked just before response headers are written. Verificando se a resposta não foi iniciada:Checking if the response has not started:

  • Fornece a capacidade de acrescentar ou substituir cabeçalhos just-in-time.Provides the ability to append or override headers just in time.
  • Não requer conhecimento do próximo middleware no pipeline.Doesn't require knowledge of the next middleware in the pipeline.
app.Use(async (context, next) =>
{
    context.Response.OnStarting(() =>
    {
        context.Response.Headers["someheader"] = "somevalue";
        return Task.CompletedTask;
    });

    await next();
});

Não chame Next () se você já tiver iniciado a gravação no corpo da respostaDo not call next() if you have already started writing to the response body

Os componentes só esperam ser chamados se for possível que eles manipulem e manipulem a resposta.Components only expect to be called if it's possible for them to handle and manipulate the response.