Blazor de ASP.NET Core con Entity Framework Core (EF Core)
Nota
Esta no es la versión más reciente de este artículo. Para la versión actual, consulte la versión de .NET 9 de este artículo.
Advertencia
Esta versión de ASP.NET Core ya no se admite. Para obtener más información, consulte la directiva de compatibilidad de .NET y .NET Core. Para la versión actual, consulte la versión de .NET 9 de este artículo.
Importante
Esta información hace referencia a un producto en versión preliminar, el cual puede sufrir importantes modificaciones antes de que se publique la versión comercial. Microsoft no proporciona ninguna garantía, expresa o implícita, con respecto a la información proporcionada aquí.
Para la versión actual, consulte la versión de .NET 9 de este artículo.
Este artículo explica cómo usar Entity Framework Core (EF Core) en aplicaciones Blazor del lado del servidor.
El Blazor del lado servidor es un marco para aplicaciones con estado. La aplicación mantiene una conexión continua con el servidor, y el estado del usuario se mantiene en la memoria del servidor en un circuito. Un ejemplo de estado del usuario son los datos contenidos en las instancias de servicio de la inserción de dependencias (DI) que se encuentran en el ámbito del circuito. El modelo de aplicación único que proporciona Blazor requiere un enfoque especial para usar Entity Framework Core.
Nota
Este artículo aborda EF Core en aplicaciones Blazor del lado del servidor. Las aplicaciones Blazor WebAssembly se ejecutan en un espacio aislado de WebAssembly que evita la mayoría de conexiones de base de datos directas. La ejecución de EF Core en Blazor WebAssembly supera el ámbito de este artículo.
Esta guía se aplica a los componentes que adoptan la representación interactiva del lado servidor (SSR interactivo) en una Blazor Web App.
Esta guía se aplica al proyecto Server
de una solución hospedada Blazor WebAssembly o a una aplicación Blazor Server.
Flujo de autenticación seguro necesario para aplicaciones de producción
Este artículo pertenece al uso de una base de datos local que no requiere autenticación de usuario. Las aplicaciones de producción deben usar el flujo de autenticación más seguro disponible. Para obtener más información sobre la autenticación para aplicaciones Blazor de prueba y producción implementadas, consulta los siguientes artículos en el nodo BlazorSeguridad y Identity.
Para los servicios de Microsoft Azure, se recomienda usar identidades administradas. Las identidades administradas proporcionan una autenticación segura en los servicios de Azure sin almacenar credenciales en el código de la aplicación. Para obtener más información, consulta los siguientes recursos:
- ¿Qué son las identidades administradas de recursos de Azure? (Documentación de Microsoft Entra)
- Documentación de servicios de Azure
Tutorial de creación de una Blazor aplicación de base de datos de películas
Para obtener una experiencia de tutorial sobre la creación de una aplicación que utiliza EF Core en las operaciones de base de datos, consulte Construcción de una aplicación de base de datos de películas de Blazor (Descripción general). En el tutorial se muestra cómo crear un objeto Blazor Web App que pueda mostrar y administrar películas en una base de datos de películas.
Acceso a la base de datos
EF Core se basa en un objeto DbContext como medio para configurar el acceso a la base de datos y actuar como una unidad de trabajo. EF Core proporciona la extensión AddDbContext para las aplicaciones de ASP.NET Core, que registra el contexto como un servicio con ámbito. En las aplicaciones Blazor del lado del servidor, los registros con ámbito de servicio pueden ser problemáticos, ya que la instancia se comparte entre los componentes del circuito del usuario. DbContext no es seguro para subprocesos y no está diseñado para su uso simultáneo. Las duraciones existentes no son adecuadas por estos motivos:
- Singleton comparte el estado entre todos los usuarios de la aplicación y lleva a un uso simultáneo inadecuado.
- Con ámbito (el valor predeterminado) supone un problema similar entre los componentes para el mismo usuario.
- Transitorio genera una nueva instancia por solicitud, pero como los componentes pueden ser de larga duración, esto da lugar a un contexto que dura más de lo previsto.
Las recomendaciones siguientes están diseñadas para proporcionar un enfoque coherente al uso de EF Core en aplicaciones Blazor del lado del servidor.
Considera usar un contexto por operación. El contexto está diseñado para la creación de instancias rápidas de baja sobrecarga:
using var context = new ProductsDatabaseContext(); return await context.Products.ToListAsync();
Usa una marca para evitar varias operaciones simultáneas:
if (Loading) { return; } try { Loading = true; ... } finally { Loading = false; }
Coloque las operaciones de base de datos después de la línea
Loading = true;
en el bloquetry
.La lógica de carga no requiere bloquear registros de base de datos porque la seguridad para subprocesos no es una preocupación. La lógica de carga se usa para deshabilitar los controles de interfaz de usuario para que los usuarios no seleccionen accidentalmente botones ni actualicen campos mientras se capturan los datos.
Si hay alguna posibilidad de que varios subprocesos tengan acceso al mismo bloque de código, inserta un generador y realiza una nueva instancia por operación. De lo contrario, la inserción y el uso del contexto suelen ser suficientes.
En el caso de las operaciones de larga duración que aprovechan el EF Core o el control de simultaneidad de , el ámbito del contexto debe ser la duración del componente.
Nuevas instancias de DbContext
La manera más rápida de crear una instancia de DbContext consiste en usar new
. Sin embargo, hay escenarios que requieren que se resuelvan dependencias adicionales:
- Uso de
DbContextOptions
para configurar el contexto. - Uso de una cadena de conexión por DbContext, como cuando se usa el modelo Identity de ASP.NET Core. Para obtener más información, consulta Servicios multiinquilino (documentación de EF Core).
Advertencia
No almacene secretos de aplicación, cadena de conexión s, credenciales, contraseñas, números de identificación personal (PIN), código C#/.NET privado o claves o tokens privados en el código del lado cliente, lo que siempre es inseguro. En entornos de prueba/ensayo y producción, el código del lado Blazor servidor y las API web deben usar flujos de autenticación seguros que eviten mantener las credenciales dentro del código del proyecto o los archivos de configuración. Fuera de las pruebas de desarrollo local, se recomienda evitar el uso de variables de entorno para almacenar datos confidenciales, ya que las variables de entorno no son el enfoque más seguro. Para las pruebas de desarrollo local, se recomienda la herramienta Secret Manager para proteger los datos confidenciales. Para obtener más información, consulte Mantener de forma segura los datos confidenciales y las credenciales.
El enfoque recomendado para crear un nuevo DbContext con dependencias es usar una fábrica. En EF Core 5.0 o posterior se proporciona un generador integrado para crear contextos.
En versiones de .NET anteriores a 5.0, use el siguiente 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);
}
}
En el generador anterior:
- ActivatorUtilities.CreateInstance satisface todas las dependencias a través del proveedor de servicios.
- IDbContextFactory<TContext> está disponible en EF Core ASP.NET Core 5.0 o posterior, por lo que la interfaz anterior solo es necesaria para ASP.NET Core 3.x.
En el ejemplo siguiente se configura SQLite y se habilita el registro de datos en una aplicación que administra los contactos. El código usa un método de extensión (AddDbContextFactory) para configurar el generador de bases de datos para la inserción de dependencias y proporcionar opciones predeterminadas:
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"));
La fábrica se inserta en los componentes para crear instancias de DbContext.
Delimitar el ámbito de un contexto de base de datos para un método de componente
El factoría se inserta en el componente:
@inject IDbContextFactory<ContactContext> DbFactory
Cree un DbContext para un método mediante la factoría (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();
}
}
}
Delimitar un contexto de base de datos a la duración del componente
Es posible que quieras crear un objeto DbContext que exista mientras dure un componente. Esto te permite usarlo como una unidad de trabajo y aprovechar características integradas como el seguimiento de cambios y la resolución de simultaneidad.
Implemente IDisposable e inserte la factoría en el componente:
@implements IDisposable
@inject IDbContextFactory<ContactContext> DbFactory
Establezca una propiedad para el DbContext:
private ContactContext? Context { get; set; }
OnInitializedAsync
se invalida para crear el DbContext:
protected override async Task OnInitializedAsync()
{
Context = DbFactory.CreateDbContext();
}
El DbContext se elimina cuando se elimina el componente:
public void Dispose() => Context?.Dispose();
Habilitar el registro de datos confidenciales
EnableSensitiveDataLogging incluye datos de la aplicación en los mensajes de excepción y en la plataforma de registro. Los datos registrados pueden incluir los valores asignados a las propiedades de las instancias de entidad y los valores de parámetro para los comandos enviados a la base de datos. El registro de datos con EnableSensitiveDataLogging es un riesgo de seguridad, ya que puede exponer contraseñas y otra Información de identificación personal (PII) cuando registra instrucciones SQL ejecutadas en la base de datos.
Se recomienda habilitar EnableSensitiveDataLogging solo para desarrollo y pruebas:
#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