ASP.NET Core Blazor com o Entity Framework Core (EF Core)
Observação
Esta não é a versão mais recente deste artigo. Para a versão atual, consulte a versão .NET 9 deste artigo.
Advertência
Esta versão do ASP.NET Core não é mais suportada. Para obter mais informações, consulte a Política de suporte do .NET e .NET Core. Para a versão atual, consulte a versão .NET 9 deste artigo.
Importante
Estas informações referem-se a um produto de pré-lançamento que pode ser substancialmente modificado antes de ser lançado comercialmente. A Microsoft não oferece garantias, expressas ou implícitas, em relação às informações fornecidas aqui.
Para a versão atual, consulte a versão .NET 9 deste artigo.
Este artigo explica como usar EF Core do Entity Framework Core () em aplicativos de Blazor do lado do servidor.
O Blazor do lado do servidor é um framework de aplicação stateful. O aplicativo mantém uma conexão contínua com o servidor e o estado do usuário é mantido na memória do servidor em um circuito . Um exemplo de estado do usuário são os dados mantidos em injeção de dependência (DI) instâncias de serviço que têm escopo para o circuito. O modelo de aplicativo exclusivo que Blazor fornece requer uma abordagem especial para usar o Entity Framework Core.
Observação
Este artigo aborda EF Core em aplicações de Blazor do lado do servidor. Blazor WebAssembly aplicações são executadas em um ambiente isolado WebAssembly que impede a maioria das conexões diretas ao banco de dados. Executar EF Core em Blazor WebAssembly está além do escopo deste artigo.
Esta orientação se aplica a componentes que adotam renderização interativa do lado do servidor (SSR interativo) em um Blazor Web App.
Esta orientação se aplica ao projeto Server
de uma solução de Blazor WebAssembly hospedada ou de um aplicativo Blazor Server.
Fluxo de autenticação seguro necessário para aplicativos de produção
Este artigo refere-se ao uso de um banco de dados local que não requer autenticação do usuário. 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 Blazor implantados, consulte os artigos na segurança Blazore no nó Identity.
Para serviços do Microsoft Azure, recomendamos o uso de identidades gerenciadas . As identidades gerenciadas são autenticadas com segurança nos serviços do Azure sem armazenar credenciais no código do aplicativo. Para obter mais informações, consulte os seguintes recursos:
- O que são identidades gerenciadas para recursos do Azure? (Documentação do Microsoft Entra)
- Documentação dos serviços do Azure
Criar um tutorial do aplicativo de banco de dados de filmes Blazor
Para obter uma experiência de tutorial criando um aplicativo que usa o EF Core para operações de banco de dados, consulte Criar um aplicativo de banco de dados de filmes Blazor (Visão geral). O tutorial mostra como criar um Blazor Web App que pode exibir e gerenciar filmes em um banco de dados de filmes.
Acesso à base de dados
EF Core conta com um DbContext como meio para configurar o acesso ao banco de dados e atuar como uma unidade de trabalho.
- Singleton compartilha o estado entre todos os utilizadores do aplicativo e leva à utilização simultânea inadequada.
- com escopo (o padrão) cria um problema semelhante entre componentes para o mesmo utilizador.
- transitório resulta em uma nova instância por solicitação; Mas, como os componentes podem ser de longa duração, isso resulta em um contexto de vida mais longa do que se pode imaginar.
As recomendações a seguir foram projetadas para fornecer uma abordagem consistente ao uso de EF Core em aplicativos Blazor do lado do servidor.
Considere o uso de um contexto por operação. O contexto foi projetado para instanciação rápida e de baixa sobrecarga:
using var context = new ProductsDatabaseContext(); return await context.Products.ToListAsync();
Use um sinalizador para evitar várias operações simultâneas:
if (Loading) { return; } try { Loading = true; ... } finally { Loading = false; }
Coloque as operações do banco de dados após a linha
Loading = true;
no blocotry
.A segurança de threads não é uma preocupação, portanto, a lógica de carregamento não requer o bloqueio de registros de banco de dados. A lógica de carregamento é usada para desabilitar os controles da interface do usuário para que os usuários não selecionem inadvertidamente botões ou atualizem campos enquanto os dados são buscados.
Se houver alguma chance de que vários threads possam aceder aos mesmos blocos de código, injete uma fábrica e crie uma nova instância por operação. Caso contrário, injetar e usar o contexto geralmente é suficiente.
Para operações de vida mais longa que aproveitam a EF Core de controle de alterações do ou controle de simultaneidade, escopo o contexto para o tempo de vida do componente.
Novas instâncias DbContext
A maneira mais rápida de criar uma nova instância de DbContext é usando new
para criar uma nova instância. No entanto, há cenários que exigem a resolução de dependências adicionais:
- Usando
DbContextOptions
para configurar o contexto. - Usando uma cadeia de conexão por DbContext, como quando utilizas o modelo Identity do ASP.NET Core. Para obter mais informações, consulte Multi-tenancy (EF Core documentation).
Advertência
Não armazene segredos de aplicativos, cadeias de conexão, credenciais, senhas, números de identificação pessoal (PINs), código C#/.NET privado ou chaves/tokens privados no código do lado do cliente, que é sempre inseguro. Em ambientes de teste/preparação e produção, o código Blazor do lado do servidor e as APIs da Web devem usar fluxos de autenticação seguros que evitem a manutenção de credenciais no código do projeto ou nos arquivos de configuração. Fora dos testes de desenvolvimento local, recomendamos evitar o uso de variáveis de ambiente para armazenar dados confidenciais, pois as variáveis de ambiente não são a abordagem mais segura. Para testes de desenvolvimento local, a ferramenta Secret Manager é recomendada para proteger dados confidenciais. Para obter mais informações, consulte Manter com segurança dados confidenciais e credenciais.
A abordagem recomendada para criar um novo DbContext com dependências é usar uma fábrica. EF Core 5.0 ou posterior fornece uma fábrica integrada para criar novos contextos.
Em versões do .NET anteriores à 5.0, use o seguinte DbContextFactory
:
using System;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
namespace BlazorServerDbContextExample.Data
{
public class DbContextFactory<TContext>
: IDbContextFactory<TContext> where TContext : DbContext
{
private readonly IServiceProvider provider;
public DbContextFactory(IServiceProvider provider)
{
this.provider = provider ?? throw new ArgumentNullException(
$"{nameof(provider)}: You must configure an instance of " +
"IServiceProvider");
}
public TContext CreateDbContext() =>
ActivatorUtilities.CreateInstance<TContext>(provider);
}
}
Na fábrica anterior:
- ActivatorUtilities.CreateInstance satisfaz quaisquer dependências através do provedor de serviços.
- IDbContextFactory<TContext> está disponível no EF Core ASP.NET Core 5.0 ou posterior, portanto, a interface anterior só é necessária para ASP.NET Core 3.x.
O exemplo a seguir configura SQLite e habilita o registro de dados em um aplicativo que gerencia contatos. O código usa um método de extensão (AddDbContextFactory) para configurar a fábrica de banco de dados para DI e fornecer opções padrão:
builder.Services.AddDbContextFactory<ContactContext>(opt =>
opt.UseSqlite($"Data Source={nameof(ContactContext.ContactsDb)}.db"));
services.AddDbContextFactory<ContactContext>(opt =>
opt.UseSqlite($"Data Source={nameof(ContactContext.ContactsDb)}.db"));
A fábrica é injetada em componentes para criar novas instâncias DbContext.
Definir o âmbito de um contexto de base de dados para um método de componente
A fábrica é inserida no componente.
@inject IDbContextFactory<ContactContext> DbFactory
Crie um DbContext para um método usando a factory (DbFactory
):
private async Task DeleteContactAsync()
{
using var context = DbFactory.CreateDbContext();
if (context.Contacts is not null)
{
var contact = await context.Contacts.FirstAsync(...);
if (contact is not null)
{
context.Contacts?.Remove(contact);
await context.SaveChangesAsync();
}
}
}
Definir o escopo de um contexto de banco de dados para o tempo de vida do componente
Você pode querer criar um DbContext que exista durante o tempo de vida de um componente. Isso permite que você o use como uma unidade de trabalho e aproveite os recursos internos, como controle de alterações e resolução de simultaneidade.
Implemente IDisposable e injete a fábrica no componente:
@implements IDisposable
@inject IDbContextFactory<ContactContext> DbFactory
Estabeleça uma propriedade para o DbContext:
private ContactContext? Context { get; set; }
OnInitializedAsync
é substituído para criar o DbContext:
protected override async Task OnInitializedAsync()
{
Context = DbFactory.CreateDbContext();
}
O DbContext é eliminado quando o componente é eliminado:
public void Dispose() => Context?.Dispose();
Habilite o registro de dados confidenciais
EnableSensitiveDataLogging inclui dados da aplicação em mensagens de exceção e registo do framework. Os dados registrados podem incluir os valores atribuídos às propriedades de instâncias de entidade e valores de parâmetro para comandos enviados ao banco de dados. O registo de dados com o EnableSensitiveDataLogging é um risco de segurança , dado que pode expor senhas e outras informações pessoalmente identificáveis (PII) ao registar instruções SQL executadas contra a base de dados.
Recomendamos habilitar apenas EnableSensitiveDataLogging para desenvolvimento e testes:
#if DEBUG
services.AddDbContextFactory<ContactContext>(opt =>
opt.UseSqlite($"Data Source={nameof(ContactContext.ContactsDb)}.db")
.EnableSensitiveDataLogging());
#else
services.AddDbContextFactory<ContactContext>(opt =>
opt.UseSqlite($"Data Source={nameof(ContactContext.ContactsDb)}.db"));
#endif