Entity Framework Core é um mapeador objeto-relacional, ou ORM, que isola o modelo de objeto do seu aplicativo do modelo de dados. Isso permite escrever código que executa operações CRUD sem se preocupar com como os dados são armazenados. Em outras palavras, você trabalha com os dados usando objetos .NET familiares.

No Entity Framework Core, o DbContext conecta as classes de domínio ao banco de dados agindo como uma ponte entre elas. Você pode aproveitar o DbContext para consultar dados em suas entidades ou salvar suas entidades no banco de dados subjacente.

Discuti os fundamentos do DbContext em um artigo anterior. Neste artigo, mergulharemos no DbContext com um pouco mais de detalhes, discutiremos o tempo de vida do DbContext e ofereceremos algumas práticas recomendadas para usar o DbContext no Entity Framework Core. O EF Core permite instanciar um DbContext de diversas maneiras. Examinaremos algumas das opções.

Para usar os exemplos de código fornecidos neste artigo, você deve ter o Visual Studio 2022 instalado em seu sistema. Se ainda não tiver uma cópia, você pode baixar o Visual Studio 2022 aqui.

Crie um projeto de API Web ASP.NET Core no Visual Studio 2022

Para criar um projeto de API Web ASP.NET Core 8 no Visual Studio 2022, siga as etapas descritas abaixo.

  1. Inicie o IDE do Visual Studio 2022.
  2. Clique em “Criar novo projeto”.
  3. Na janela “Criar novo projeto”, selecione “API Web ASP.NET Core” na lista de modelos exibida.
  4. Clique em Avançar.
  5. Na janela “Configure seu novo projeto”, especifique o nome e o local do novo projeto.
  6. Opcionalmente, marque a caixa de seleção “Colocar solução e projeto no mesmo diretório”, dependendo de suas preferências.
  7. Clique em Avançar.
  8. Na janela “Informações adicionais” mostrada a seguir, selecione “.NET 8.0 (Long Term Support)” como a versão da estrutura e certifique-se de que a caixa “Usar controladores” esteja desmarcada. Usaremos APIs mínimas neste projeto.
  9. Em outra parte da janela “Informações adicionais”, deixe o “Tipo de autenticação” definido como “Nenhum” (o padrão) e certifique-se de que as caixas de seleção “Ativar suporte à API aberta”, “Configurar para HTTPS” e “Ativar Docker” permaneçam desmarcadas. Não usaremos nenhum desses recursos aqui.
  10. Clique em Criar.

Usaremos este projeto de API Web ASP.NET Core para trabalhar com DbContext nas seções abaixo.

O que é DbContext? Por que é necessário?

Ao trabalhar com o Entity Framework Core, o DbContext representa uma sessão de conexão com o banco de dados. Funciona como uma unidade de trabalho, permitindo aos desenvolvedores monitorar e controlar as alterações feitas nas entidades antes de salvá-las no banco de dados. Usamos o DbContext para recuperar dados de nossas entidades ou persistir nossas entidades no banco de dados.

A classe DbContext no EF Core segue os padrões Unidade de Trabalho e Repositório. Ele fornece uma maneira de encapsular a lógica do banco de dados dentro do aplicativo, facilitando o trabalho com o banco de dados e mantendo a reutilização do código e a separação de interesses.

O DbContext no EF Core tem diversas responsabilidades:

  • Gerenciando conexões
  • Consultando dados do banco de dados
  • Salvando dados no banco de dados
  • Controle de simultaneidade
  • Rastreamento de alterações
  • Cache
  • Gerenciamento de transações

A vida útil do DbContext

A classe DbContext no Entity Framework Core desempenha um papel crucial na facilitação da conexão entre a aplicação e o banco de dados, fornecendo suporte para acesso a dados, rastreamento de alterações e gerenciamento de transações. O tempo de vida de uma instância DbContext começa quando ela é instanciada e termina quando é descartada.

Aqui está a sequência de eventos em uma unidade de trabalho típica usando o EF Core:

  1. Uma instância DbContext é criada.
  2. As entidades são rastreadas usando esta instância.
  3. As alterações são feitas nas entidades rastreadas.
  4. O método SaveChanges é invocado para armazenar as entidades na memória do banco de dados subjacente.
  5. O objeto DbContext é descartado ou coletado como lixo quando não é mais necessário pelo aplicativo.

Evite usar DbContext em instruções de uso

Uma instância DbContext deve ser descartada quando não for mais necessária para liberar recursos não gerenciados e evitar vazamentos de memória. No entanto, não é uma prática recomendada descartar instâncias de DbContext explicitamente ou usar DbContext dentro de um using declaração.

Veja por que você não deve descartar suas instâncias DbContext:

  • Ao descartar um objeto DbContext, você pode ter uma ou mais entidades que não podem ser salvas no banco de dados.
  • Instanciar e liberar objetos DbContext pode ser caro, principalmente ao configurar uma nova conexão de banco de dados.
  • Remover o DbContext dentro de um bloco using após cada uso pode resultar em sobrecarga evitável e desempenho reduzido.
  • Descartar o objeto DbContext quando as modificações do banco de dados estão pendentes ou quando você ainda espera usar o contexto pode causar problemas ou comportamento inesperado.
  • O descarte prematuro das instâncias DbContext pode interferir no controle de alterações, dificultando ou impossibilitando atualizações ou consultas adicionais.

Ao invés de usar using blocos para descartar as instâncias DbContext em seu aplicativo, considere aproveitar a injeção de dependência para gerenciar seu tempo de vida. O uso da injeção de dependência garantirá que o DbContext seja criado e descartado adequadamente, dependendo do ciclo de vida do aplicativo ou do escopo da operação.

Crie uma nova instância DbContext no EF Core

Não existe uma regra específica para criar uma instância DbContext. Os requisitos da sua aplicação devem determinar qual abordagem você adotará. Cada uma das abordagens ilustradas abaixo tem seus casos de uso específicos – nenhuma delas é melhor que as outras.

Você pode estender a classe DbContext no EF Core para criar sua própria classe DbContext conforme mostrado abaixo.

public class IDGDbContext : DbContext
{
    public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
        : base(options)
    {
    }
}

Da mesma forma, você poderia instanciar uma classe DbContextOptionsBuilder e usar essa instância para criar uma instância de DbContextOptions. Essa instância DbContextOptions poderia então ser passada para o construtor DbContext. Essa abordagem ajuda você a criar explicitamente uma instância DbContext.

Use injeção de dependência para criar instâncias DbContext

Como alternativa, você pode criar instâncias DbContext por meio de injeção de dependência (DI) configurando sua instância DbContext usando o método AddDbContext conforme mostrado abaixo.

services.AddDbContext<IDGDbContext>(
        options => options.UseSqlServer("name=ConnectionStrings:DefaultConnection"));

Você pode então aproveitar a injeção de construtor em seu controlador para recuperar uma instância de DbContext conforme mostrado abaixo.

public class IDGController
{
    private readonly IDGDbContext _dbContext;
    public IDGController(IDGDbContext dbContext)
    {
        _dbContext = dbContext;
    }
}

Normalmente, um ciclo de solicitação-resposta HTTP representa uma unidade de trabalho em aplicações web. Com DI, podemos criar uma instância DbContext para cada solicitação e descartá-la quando a solicitação terminar.

Prefiro usar um contêiner DI para instanciar e configurar o DbContext porque o contêiner DI gerencia as instâncias e tempos de vida do DbContext para você, aliviando o trabalho de gerenciar essas instâncias explicitamente.

Inicialize DbContext no método OnConfigurando

Uma terceira opção é criar uma instância DbContext substituindo o método OnConposing em sua classe DbContext personalizada. Você pode então aproveitar o construtor DbContext para passar informações de configuração, como uma cadeia de conexão.

O trecho de código abaixo mostra como você pode inicializar DbContext no método OnConposing de sua classe DbContext personalizada.

public class IDGDbContext : DbContext
{
    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
        optionsBuilder.UseSqlServer("Specify your database connection string here.");
    }
}

Registre uma fábrica para criar uma instância DbContext

Você também pode criar uma instância de DbContext usando uma fábrica. Uma fábrica é útil quando seu aplicativo precisa executar várias unidades de trabalho em um escopo específico.

Nesse caso, você pode usar o método AddDbContextFactory para registrar uma fábrica e criar seus objetos DbContext conforme mostrado no trecho de código fornecido a seguir.

services.AddDbContextFactory<IDGDbContext>(
        options =>
            options.UseSqlServer("Specify your database connection string here."));

Você pode então usar a injeção de construtor em seu controlador para construir instâncias de DbContext conforme mostrado abaixo.

private readonly IDbContextFactory<IDGDbContext> _dbContextFactory;
public IDGController(IDbContextFactory<IDGDbContext> dbContextFactory)
{
    _dbContextFactory = dbContextFactory;
}

Você pode ativar o registro de dados confidenciais para incluir dados do aplicativo quando exceções forem registradas em seu aplicativo. O trecho de código a seguir mostra como isso pode ser feito.

optionsBuilder
            .EnableSensitiveDataLogging()
            .UseSqlServer("Specify your database connection string here.");

Finalmente, observe que múltiplas operações paralelas não podem ser executadas simultaneamente na mesma instância DbContext. Isso se refere tanto à execução paralela de consultas assíncronas quanto a qualquer uso explícito de vários threads da instância simultaneamente. Portanto, é recomendado que as operações paralelas sejam executadas usando instâncias separadas do DbContext. Além disso, você nunca deve compartilhar instâncias de DbContext entre threads porque isso não é seguro para threads.