Usando una fábrica de contexto de base de datos en Blazor server.
Estoy teniendo algunos problemas para configurar la conexión de base datos EF para mi aplicación Blazor del lado del servidor. Funcionaba con la configuración estándar DbContext
hasta que noté que había problemas con la conexión que no se cerraba correctamente debido a que Blazor usaba el mismo contexto en todo momento. Mi investigación me llevó a ver DbContextFactory
, pero la interfaz IDbContextFactory
está obsoleta y se prefiere IDesignTimeDbContextFactory
.
He configurado una clase para implementar la interfaz:
public class FIS2ContextFactory : IDesignTimeDbContextFactory<FIS2_DbContext>
{
private readonly DbContextOptions<FIS2_FranklinContext_AutoGenerated> options;
public FIS2ContextFactory(DbContextOptions<FIS2_FranklinContext_AutoGenerated> contextOptions)
{
options = contextOptions;
}
public FIS2_DbContext CreateDbContext(string[] args)
{
return new FIS2_DbContext(options);
}
}
El DbContext que quiero utilizar es el siguiente, que hereda y amplía el DbContext generado por EF Power Tools:
public partial class FIS2_DbContext : FIS2_FranklinContext_AutoGenerated
{
public FIS2_DbContext()
{
}
public FIS2_DbContext(DbContextOptions<FIS2_FranklinContext_AutoGenerated> options) : base(options)
{
}
public virtual DbSet<StudentBasicDetailsWithCurrentTg> StudentBasicDetailsWithCurrentTgs { get; set; }
public virtual DbSet<CurriculumSearchBasicDetails> CurriculumSearchBasicDetails { get; set; }
public virtual DbSet<StudentAllEnrolments> StudentAllEnrolments { get; set; }
}
En mi startup.cs
lo tengo configurado así en el método ConfigureServices
:
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContextFactory<FIS2_DbContext>(options => options.UseSqlServer(Configuration.GetConnectionString("FIS2")));
services.AddRazorPages();
services.AddServerSideBlazor();
services.AddScoped<IFileService, FileService>();
services.AddScoped<IEmailService, EmailService>();
services.AddScoped<ITimetableService, TimetableService>();
services.AddScoped<ICurriculumService, CurriculumServiceEf>();
services.AddScoped<IStudentService, StudentServiceEf>();
services.AddScoped<ICollectionService, CollectionsServiceEf>();
services.AddHttpContextAccessor();
services.AddHttpClient();
services.AddAuthenticationCore();
services.AddAuthentication(IISDefaults.AuthenticationScheme);
services.AddAuthorization();
services.AddSyncfusionBlazor();
services.AddScoped<SessionState>();
}
Mi problema es que cuando se trata de configurar los servicios que utilizan esta conexión de base de datos, me encuentro con este mensaje de error en el program.cs
:
Algunos servicios no pueden construirse (Error al validar el descriptor de servicio ‘ServiceType: DataLibrary.Data.Interfaces.ITimetableService Lifetime: Scoped ImplementationType: DataLibrary.Data.BusinessLayer.TimetableService’: No se puede resolver el servicio para el tipo ‘DataLibrary.Models.FIS2ContextFactory’ mientras se intenta activar ‘DataLibrary.Data.BusinessLayer.TimetableService’. ) (Error al validar el descriptor de servicio ‘ServiceType: DataLibrary.Data.Interfaces.ICurriculumService Lifetime: Scoped ImplementationType: DataLibrary.Data.BusinessLayer.CurriculumServiceEf’: No se puede resolver el servicio para el tipo ‘DataLibrary.Models.FIS2ContextFactory’ mientras se intenta activar ‘DataLibrary.Data.BusinessLayer.CurriculumServiceEf’.) (Error al validar el descriptor de servicio ‘ServiceType: DataLibrary.Data.Interfaces.IStudentService Lifetime: Scoped ImplementationType: DataLibrary.Data.BusinessLayer.StudentServiceEf’: No se puede resolver el servicio para el tipo ‘DataLibrary.Models.FIS2ContextFactory’ mientras se intenta activar ‘DataLibrary.Data.BusinessLayer.StudentServiceEf’.) (Error al validar el descriptor de servicio ‘ServiceType: DataLibrary.Data.Interfaces.ICollectionService Lifetime: Scoped ImplementationType: DataLibrary.Data.BusinessLayer.CollectionsServiceEf’: No se puede resolver el servicio para el tipo ‘DataLibrary.Models.FIS2ContextFactory’ mientras se intenta activar ‘DataLibrary.Data.BusinessLayer.CollectionsServiceEf’.)
Para referencia, aquí está un ejemplo de cómo se configura TimetableService
(los otros se instancian de la misma manera):
using DataLibrary.Data.Interfaces;
using DataLibrary.Models;
using DataLibrary.Models.timetable;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace DataLibrary.Data.BusinessLayer
{
public class TimetableService : ITimetableService
{
private FIS2ContextFactory _contextFactory;
public TimetableService(FIS2ContextFactory db)
{
_contextFactory = db;
}
public async Task<List<spGetHolidaysBetweenDatesResult>> GetHolidaysBetweenDatesAsync(DateTime startDate, DateTime endDate)
{
string[] args = { "" };
var _db = _contextFactory.CreateDbContext(args);
var procedures = _db.Procedures;
return await procedures.spGetHolidaysBetweenDatesAsync(startDate, endDate);
}
public async Task<List<PeriodsBetweenDates>> GetPeriodsBetweenDatesAsync(DateTime startDate, DateTime endDate)
{
string[] args = { "" };
var _db = _contextFactory.CreateDbContext(args);
var procedures = _db.Procedures;
var toReturn = new List<PeriodsBetweenDates>();
var results = await procedures.spGetPeriodsBetweenDatesAsync(startDate, endDate);
foreach (var item in results)
{
toReturn.Add(new PeriodsBetweenDates(item.Date, item.Timetable, item.BlockCode, item.StartTime, item.EndTime));
}
return toReturn;
}
public async Task<List<StudentTimetable>> GetStudentTimetableAsync(DateTime startDate, DateTime endDate, string studentID)
{
string[] args = { "" };
var _db = _contextFactory.CreateDbContext(args);
var procedures = _db.Procedures;
var results = await procedures.spGetStudentTimetableAsync(startDate, endDate, studentID);
List<StudentTimetable> studentTimetables = new List<StudentTimetable>();
foreach (var item in results)
{
studentTimetables.Add(JsonConvert.DeserializeObject<StudentTimetable>(item.timetable));
}
return studentTimetables;
}
}
}
¿Es debido a que estoy usando el método incorrecto para crear la fábrica de contexto en la puesta en marcha, o es algo más adelante que tengo mal?
davy.ai
El mensaje de error sugiere que
DataLibrary.Models.FIS2ContextFactory
no se puede resolver cuando se están construyendo los servicios que lo utilizan. Esto podría ser porque no está registrado en el contenedor de inyección de dependencias o porque está registrado incorrectamente.En el método
ConfigureServices
del archivostartup.cs
, se utiliza el métodoAddDbContextFactory
para registrarFIS2_DbContext
en el contenedor de inyección de dependencias. Este método toma una función de fábrica que crea instancias deDbContext
cuando se necesitan. Sin embargo, cuando se están construyendo los servicios, las clasesTimetableService
,CurriculumServiceEf
,StudentServiceEf
yCollectionsServiceEf
buscan una instancia deFIS2ContextFactory
en sí y no elDbContext
que crea.Para solucionar este problema,
FIS2ContextFactory
en sí necesita ser registrado en el contenedor de inyección de dependencias. Esto se puede hacer agregándolo al métodoConfigureServices
:Esto registra una instancia singleton de
FIS2ContextFactory
en el contenedor de inyección de dependencias. Una vez hecho esto, los otros servicios que lo utilizan pueden ser actualizados para aceptar una instancia deFIS2ContextFactory
directamente:Al cambiar el constructor de cada servicio para aceptar una instancia de
FIS2ContextFactory
directamente, los servicios pueden ser construidos correctamente por el contenedor de inyección de dependencias con la instancia correcta deFIS2_DbContext
.