Nota
O acesso a esta página requer autorização. Podes tentar iniciar sessão ou mudar de diretório.
O acesso a esta página requer autorização. Podes tentar mudar de diretório.
Este artigo mostra padrões básicos para inicialização e configuração de uma DbContext instância.
Advertência
Este artigo usa um banco de dados local que não exige que o usuário seja autenticado. Os aplicativos de produção devem usar o fluxo de autenticação mais seguro disponível. Para obter mais informações sobre autenticação para aplicativos de teste e produção implantados, consulte Fluxos de autenticação seguros.
A vida útil do DbContext
A vida útil de um DbContext começa quando a instância é criada e termina quando a instância é descartada. Uma DbContext instância é concebida para ser usada numa únicaunidade de trabalho. Isto significa que a vida útil de uma DbContext instância é geralmente muito curta.
Sugestão
Para citar Martin Fowler no link acima, "Uma Unidade de Trabalho regista tudo o que faz durante uma transação comercial que pode afetar a base de dados. Quando terminares, ela resolve tudo o que precisa de ser feito para alterar a base de dados como resultado do teu trabalho."
Uma unidade de trabalho típica ao utilizar o Entity Framework Core (EF Core) envolve:
- Criação de uma
DbContextinstância - Rastreio de instâncias de entidades pelo contexto. As entidades passam a ser rastreadas por
- São feitas alterações às entidades rastreadas conforme necessário para implementar a regra de negócio
- SaveChanges ou SaveChangesAsync é chamado. O EF Core deteta as alterações feitas e grava-as na base de dados.
- A
DbContextinstância é descartada
Importante
- É importante descartar o DbContext após o uso. Isto garante qualquer:
- Recursos não geridos são liberados.
- Eventos ou outros ganchos não estão registados. O desregisto previne fugas de memória quando a instância permanece referenciada.
- O DbContext não é seguro para threads. Não partilhes contextos entre tópicos. Certifica-te de aguardar todas as chamadas assíncronas antes de continuares a usar a instância de contexto.
- Uma exceção InvalidOperationException lançada pelo código do EF Core pode colocar o contexto num estado irrecuperável. Tais exceções indicam um erro do programa e não são concebidas para serem recuperadas.
DbContext na injeção de dependências para ASP.NET Core
Em muitas aplicações web, cada pedido HTTP corresponde a uma única unidade de trabalho. Isto faz com que associar a vida útil do contexto à do pedido seja um bom padrão para aplicações web.
ASP.NET As aplicações principais são configuradas usando injeção de dependências. O EF Core pode ser adicionado a esta configuração usando AddDbContext em Program.cs. Por exemplo:
var connectionString =
builder.Configuration.GetConnectionString("DefaultConnection")
?? throw new InvalidOperationException("Connection string"
+ "'DefaultConnection' not found.");
builder.Services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(connectionString));
O código anterior regista ApplicationDbContext, uma subclasse de DbContext, como um serviço com âmbito no fornecedor de serviços de aplicação ASP.NET Core. O fornecedor de serviços é também conhecido como contentor de injeção de dependência. O contexto está configurado para usar o provedor de base de dados SQL Server e lê a cadeia de ligação a partir da configuração do ASP.NET Core.
A ApplicationDbContext classe deve expor um construtor público com um DbContextOptions<ApplicationDbContext> parâmetro. É assim que a configuração de contexto de AddDbContext é passada para o DbContext. Por exemplo:
public class ApplicationDbContext : DbContext
{
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
: base(options)
{
}
}
ApplicationDbContext podem ser usados em controladores ASP.NET Core ou outros serviços através da injeção de construtores:
public class MyController
{
private readonly ApplicationDbContext _context;
public MyController(ApplicationDbContext context)
{
_context = context;
}
}
O resultado final é uma ApplicationDbContext instância criada para cada pedido e passada ao controlador para realizar uma unidade de trabalho antes de ser descartada quando o pedido termina.
Leia mais neste artigo para saber mais sobre as opções de configuração. Consulte Injeção de dependências no ASP.NET Core para mais informações.
Inicialização básica do DbContext com 'new'
DbContext as instâncias podem ser construídas com new em C#. A configuração pode ser realizada sobrescrevendo o OnConfiguring método ou passando opções ao construtor. Por exemplo:
public class ApplicationDbContext : DbContext
{
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlServer(
@"Server=(localdb)\mssqllocaldb;Database=Test;ConnectRetryCount=0");
}
}
Este padrão também facilita a passagem de configurações como a string de ligação através do DbContext construtor. Por exemplo:
public class ApplicationDbContext : DbContext
{
private readonly string _connectionString;
public ApplicationDbContext(string connectionString)
{
_connectionString = connectionString;
}
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlServer(_connectionString);
}
}
Alternativamente, DbContextOptionsBuilder pode ser usado para criar um DbContextOptions objeto que é depois passado ao DbContext construtor. Isto permite que um DbContext configurado para injeção de dependências também seja construído explicitamente. Por exemplo, ao utilizar ApplicationDbContext definido para aplicações web ASP.NET Core acima:
public class ApplicationDbContext : DbContext
{
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
: base(options)
{
}
}
O DbContextOptions pode ser criado e o construtor pode ser chamado explicitamente:
var contextOptions = new DbContextOptionsBuilder<ApplicationDbContext>()
.UseSqlServer(@"Server=(localdb)\mssqllocaldb;Database=Test;ConnectRetryCount=0")
.Options;
using var context = new ApplicationDbContext(contextOptions);
Utilize uma fábrica de DbContext
Alguns tipos de aplicação (por exemplo, ASP.NET Core Blazor) usam injeção de dependências, mas não criam um âmbito de serviço que se alinhe com a vida útil desejada DbContext . Mesmo quando tal alinhamento existe, a aplicação pode precisar de realizar múltiplas unidades de trabalho dentro deste âmbito. Por exemplo, múltiplas unidades de trabalho dentro de um único pedido HTTP.
Nestes casos, AddDbContextFactory pode ser usado para registar uma fábrica para a criação de instâncias de DbContext. Por exemplo:
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContextFactory<ApplicationDbContext>(
options => options.UseSqlServer(
@"Server=(localdb)\mssqllocaldb;Database=Test;ConnectRetryCount=0"));
}
A ApplicationDbContext classe deve expor um construtor público com um DbContextOptions<ApplicationDbContext> parâmetro. Este é o mesmo padrão usado na secção tradicional ASP.NET Core acima.
public class ApplicationDbContext : DbContext
{
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
: base(options)
{
}
}
A DbContextFactory fábrica pode então ser utilizada noutros serviços através da injeção de construtores. Por exemplo:
private readonly IDbContextFactory<ApplicationDbContext> _contextFactory;
public MyController(IDbContextFactory<ApplicationDbContext> contextFactory)
{
_contextFactory = contextFactory;
}
A fábrica injetada pode ser usada então para construir instâncias de DbContext no código de serviço. Por exemplo:
public async Task DoSomething()
{
using (var context = _contextFactory.CreateDbContext())
{
// ...
}
}
Observe que as DbContext instâncias criadas desta forma não são geridas pelo fornecedor de serviços da aplicação e, por isso, devem ser descartadas pela aplicação.
Consulte ASP.NET Core Blazor Server com Entity Framework Core para mais informações sobre a utilização do EF Core com Blazor.
DbContextOptions
O ponto de partida para todas DbContext as configurações é DbContextOptionsBuilder. Existem três formas de obter esta ferramenta de criação:
- Em
AddDbContexte métodos relacionados - Em
OnConfiguring - Construído explicitamente com
new
Exemplos de cada um destes são apresentados nas secções anteriores. A mesma configuração pode ser aplicada independentemente da origem do construtor. Além disso, OnConfiguring é sempre chamado independentemente de como o contexto é construído. Isto significa OnConfiguring que pode ser usado para realizar configurações adicionais mesmo quando AddDbContext está a ser utilizado.
Configuração do fornecedor de base de dados
Cada DbContext instância deve ser configurada para usar apenas um fornecedor de base de dados. (Diferentes instâncias de um DbContext subtipo podem ser usadas com diferentes fornecedores de bases de dados, mas uma única instância deve usar apenas uma.) Um fornecedor de base de dados é configurado usando uma chamada específica Use* . Por exemplo, para usar o fornecedor de base de dados SQL Server:
public class ApplicationDbContext : DbContext
{
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlServer(
@"Server=(localdb)\mssqllocaldb;Database=Test;ConnectRetryCount=0");
}
}
Estes Use* métodos são métodos de extensão implementados pelo fornecedor da base de dados. Isto significa que o pacote NuGet do fornecedor de bases de dados deve ser instalado antes de o método de extensão poder ser utilizado.
Sugestão
Os fornecedores de bases de dados EF Core fazem uso extensivo de métodos de extensão. Se o compilador indicar que um método não pode ser encontrado, certifique-se de que o pacote NuGet do fornecedor está instalado e que tem using Microsoft.EntityFrameworkCore; no seu código.
A tabela seguinte contém exemplos de fornecedores comuns de bases de dados.
| Sistema de bases de dados | Exemplo de configuração | Pacote NuGet |
|---|---|---|
| SQL Server ou Azure SQL | .UseSqlServer(connectionString) |
Microsoft.EntityFrameworkCore.SqlServer |
| Azure Cosmos DB | .UseCosmos(connectionString, databaseName) |
Microsoft.EntityFrameworkCore.Cosmos |
| SQLite | .UseSqlite(connectionString) |
Microsoft.EntityFrameworkCore.Sqlite |
| Banco de dados na memória EF Core | .UseInMemoryDatabase(databaseName) |
Microsoft.EntityFrameworkCore.InMemory |
| PostgreSQL* | .UseNpgsql(connectionString) |
Npgsql.EntityFrameworkCore.PostgreSQL |
| MySQL/MariaDB* | .UseMySql(connectionString) |
Pomelo.EntityFrameworkCore.MySql |
| Oráculo* | .UseOracle(connectionString) |
Oracle.EntityFrameworkCore |
*Estes fornecedores de bases de dados não são fornecidos pela Microsoft. Consulte Fornecedores de Bases de Dados para mais informações sobre fornecedores de bases de dados.
Advertência
A base de dados EF Core em memória não foi concebida para uso em produção. Além disso, pode não ser a melhor escolha, mesmo para testes. Consulte Código de Teste que utiliza o EF Core para mais informações.
Consulte Cadeias de Ligação para mais informações sobre o uso de cadeias de ligação com EF Core.
A configuração opcional específica do fornecedor de base de dados é realizada num construtor adicional específico para cada fornecedor. Por exemplo, usar EnableRetryOnFailure para configurar novas tentativas para resiliência de ligação ao ligar ao Azure SQL.
public class ApplicationDbContext : DbContext
{
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder
.UseSqlServer(
@"Server=(localdb)\mssqllocaldb;Database=Test",
providerOptions => { providerOptions.EnableRetryOnFailure(); });
}
}
Sugestão
O mesmo fornecedor de bases de dados é utilizado para SQL Server e Azure SQL. No entanto, recomenda-se que a resiliência da ligação seja utilizada ao ligar ao SQL Azure.
Consulte Fornecedores de Bases de Dados para mais informações sobre configuração específica de cada fornecedor.
Outras configurações do DbContext
Outras DbContext configurações podem ser encadeadas antes ou depois da chamada de Use* (não importa qual). Por exemplo, ativar o registo de dados sensíveis:
public class ApplicationDbContext : DbContext
{
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder
.EnableSensitiveDataLogging()
.UseSqlServer(@"Server=(localdb)\mssqllocaldb;Database=Test;ConnectRetryCount=0");
}
}
A tabela seguinte contém exemplos de métodos comuns chamados em DbContextOptionsBuilder.
| Método DbContextOptionsBuilder | O que faz | Mais informações |
|---|---|---|
| UseQueryTrackingBehavior | Define o comportamento padrão de rastreamento para consultas | Comportamento de Rastreio de Consultas |
| LogTo | Uma forma simples de obter registos do EF Core | Registo, Eventos e Diagnósticos |
| UseLoggerFactory | Registos numa Microsoft.Extensions.Logging fábrica |
Registo, Eventos e Diagnósticos |
| EnableSensitiveDataLogging | Inclui dados de aplicação em exceções e registos | Registo, Eventos e Diagnósticos |
| EnableDetailedErrors | Erros de consulta mais detalhados (à custa do desempenho) | Registo, Eventos e Diagnósticos |
| ConfigureWarnings | Ignora ou atira para avisos e outros eventos | Registo, Eventos e Diagnósticos |
| AddInterceptors | Regista interceptores do EF Core | Registo, Eventos e Diagnósticos |
| EnableServiceProviderCaching | Controla a cache do fornecedor de serviços interno | Cache do Provedor de Serviços |
| UseMemoryCache | Configura a cache de memória usada pelo EF Core | Integração de Cache de Memória |
| UseLazyLoadingProxies | Use proxies dinâmicos para carregamento preguiçoso | Carregamento Preguiçoso |
| UseChangeTrackingProxies | Use proxies dinâmicos para acompanhamento de alterações | Brevemente... |
Observação
UseLazyLoadingProxies e UseChangeTrackingProxies são métodos de extensão do pacote NuGet Microsoft.EntityFrameworkCore.Proxyes . Este tipo de chamada ".UseSomething()" é a forma recomendada de configurar e/ou utilizar extensões EF Core contidas noutros pacotes.
DbContextOptions Versus DbContextOptions<TContext>
A maioria DbContext das subclasses que aceitam a DbContextOptions devem usar a variação genéricaDbContextOptions<TContext> . Por exemplo:
public sealed class SealedApplicationDbContext : DbContext
{
public SealedApplicationDbContext(DbContextOptions<SealedApplicationDbContext> contextOptions)
: base(contextOptions)
{
}
}
Isto garante que as opções corretas para o subtipo específico DbContext são resolvidas a partir da injeção de dependências, mesmo quando múltiplos DbContext subtipos estão registados.
Sugestão
O seu DbContext não precisa de ser selado, mas o selamento é a melhor prática para classes que não foram concebidas para serem herdadas.
No entanto, se o DbContext subtipo for destinado a ser herdado, então deverá expor um construtor protegido que aceite um DbContextOptions não-genérico. Por exemplo:
public abstract class ApplicationDbContextBase : DbContext
{
protected ApplicationDbContextBase(DbContextOptions contextOptions)
: base(contextOptions)
{
}
}
Isto permite que múltiplas subclasses concretas chamem este construtor base usando as suas diferentes instâncias genéricas DbContextOptions<TContext>. Por exemplo:
public sealed class ApplicationDbContext1 : ApplicationDbContextBase
{
public ApplicationDbContext1(DbContextOptions<ApplicationDbContext1> contextOptions)
: base(contextOptions)
{
}
}
public sealed class ApplicationDbContext2 : ApplicationDbContextBase
{
public ApplicationDbContext2(DbContextOptions<ApplicationDbContext2> contextOptions)
: base(contextOptions)
{
}
}
Note que este é exatamente o mesmo padrão que ao herdar diretamente de DbContext . Ou seja, o próprio construtor DbContext aceita um DbContextOptions não genérico por esse motivo.
Uma DbContext subclasse destinada a ser simultaneamente instanciada e herdada de deve expor ambas as formas de construtor. Por exemplo:
public class ApplicationDbContext : DbContext
{
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> contextOptions)
: base(contextOptions)
{
}
protected ApplicationDbContext(DbContextOptions contextOptions)
: base(contextOptions)
{
}
}
Configuração do DbContext em tempo de desenvolvimento
As ferramentas de design do EF Core, como as migrações do EF Core , precisam de ser capazes de descobrir e criar uma instância funcional de um DbContext tipo para recolher detalhes sobre os tipos de entidade da aplicação e como estes se mapeiam para um esquema de base de dados. Este processo pode ser automático desde que a ferramenta consiga criar facilmente o DbContext de maneira que seja configurado de forma semelhante à configuração em tempo de execução.
Embora qualquer padrão que forneça a informação de configuração necessária para o DbContext possa funcionar em tempo de execução, ferramentas que requerem o uso de um DbContext em tempo de design só podem funcionar com um número limitado de padrões. Esta é abordada com mais detalhe em Criação de Contexto em Tempo de Design.
Evitar problemas de concorrência no DbContext
O Entity Framework Core não suporta a execução de múltiplas operações paralelas na mesma DbContext instância. Isto inclui tanto a execução paralela de consultas assíncronas como qualquer utilização explícita e concorrente de múltiplos threads. Execute sempre await chamadas assíncronas imediatamente, ou use instâncias separadas DbContext para operações que sejam executadas em paralelo.
Quando o EF Core deteta uma tentativa de usar instâncias de DbContext em simultâneo, verá uma mensagem de InvalidOperationException como esta:
Uma segunda operação começou neste contexto antes de uma operação anterior ser concluída. Isto geralmente ocorre quando diferentes threads utilizam a mesma instância do DbContext; entretanto, não há garantia de que os membros da instância sejam seguros para uso em múltiplas threads.
Quando o acesso concorrente não é detetado, pode resultar em comportamentos indefinidos, falhas de aplicações e corrupção de dados.
Existem erros comuns que podem, inadvertidamente, causar acesso concorrente na mesma DbContext instância:
Armadilhas na operação assíncrona
Os métodos assíncronos permitem ao EF Core iniciar operações que acedem à base de dados de forma não bloqueante. Mas se um chamador não aguardar a conclusão de um destes métodos, e proceder a realizar outras operações no DbContext, o estado do DbContext pode ser, (e muito provavelmente será) corrompido.
Aguarde sempre imediatamente os métodos assíncronos do EF Core.
Partilha implícita de instâncias de DbContext via injeção de dependências
O AddDbContext método de extensão regista DbContext tipos com um ciclo de vida delimitado por padrão.
Isto está protegido contra problemas de acesso concorrente na maioria das aplicações ASP.NET Core porque existe apenas uma thread a executar cada pedido cliente de cada vez, e porque cada pedido recebe um âmbito de injeção de dependência separado (e, portanto, uma instância separada DbContext ). Para o modelo de hospedagem Blazor Server, é usada uma requisição lógica para manter o circuito de utilizador do Blazor, e assim apenas uma instância de DbContext está disponível por circuito de utilizador se o escopo de injeção padrão for utilizado.
Qualquer código que execute explicitamente múltiplos threads em paralelo deve garantir que DbContext as instâncias nunca sejam acedidas concurrentemente.
Usando injeção de dependências, isto pode ser conseguido registando o contexto como com escopo e criando escopos (usando IServiceScopeFactory) para cada thread, ou registando o DbContext como transitório (usando a sobrecarga de AddDbContext que aceita um parâmetro ServiceLifetime).
Ler mais
- Leia Injeção de Dependências para saber mais sobre como usar DI.
- Leia Testing para mais informações.