I'm having a few issues setting up the EF database connection for my server side Blazor app. It was working with the standard DbContext
setup until I noticed a few issues with connections not closing properly due to the nature of Blazor using the same context throughout. My research led me to look at DbContextFactory
, but the interface IDbContextFactory
is now deprecated in favour of IDesignTimeDbContextFactory
.
I've set up a class to implement the interface:
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);
}
}
The DbContext I'm wanting to use is this, which inherits and expands on the DbContext generated by 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; }
}
In my startup.cs
I have it set up like this in the ConfigureServices
method:
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>();
}
My issue is that when it gets to setting up the services that utilise this database connection, I am met with this error message in the program.cs
:
Some services are not able to be constructed (Error while validating the service descriptor 'ServiceType: DataLibrary.Data.Interfaces.ITimetableService Lifetime: Scoped ImplementationType: DataLibrary.Data.BusinessLayer.TimetableService': Unable to resolve service for type 'DataLibrary.Models.FIS2ContextFactory' while attempting to activate 'DataLibrary.Data.BusinessLayer.TimetableService'.) (Error while validating the service descriptor 'ServiceType: DataLibrary.Data.Interfaces.ICurriculumService Lifetime: Scoped ImplementationType: DataLibrary.Data.BusinessLayer.CurriculumServiceEf': Unable to resolve service for type 'DataLibrary.Models.FIS2ContextFactory' while attempting to activate 'DataLibrary.Data.BusinessLayer.CurriculumServiceEf'.) (Error while validating the service descriptor 'ServiceType: DataLibrary.Data.Interfaces.IStudentService Lifetime: Scoped ImplementationType: DataLibrary.Data.BusinessLayer.StudentServiceEf': Unable to resolve service for type 'DataLibrary.Models.FIS2ContextFactory' while attempting to activate 'DataLibrary.Data.BusinessLayer.StudentServiceEf'.) (Error while validating the service descriptor 'ServiceType: DataLibrary.Data.Interfaces.ICollectionService Lifetime: Scoped ImplementationType: DataLibrary.Data.BusinessLayer.CollectionsServiceEf': Unable to resolve service for type 'DataLibrary.Models.FIS2ContextFactory' while attempting to activate 'DataLibrary.Data.BusinessLayer.CollectionsServiceEf'.)
For reference, here is an example of how the TimetableService
is set up (the others are instantiated in the same way):
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;
}
}
}
Is it because I'm using the wrong method to create the context factory in the startup, or is it something later on that I've got wrong?
CodePudding user response:
If you want to resolve a specific factory type, you must register with this overload, AddDbContextFactory<TContext,TFactory>
documented here:
This overload allows a specific implementation of IDbContextFactory to be registered instead of using the default factory shipped with EF Core.
so
services.AddDbContextFactory<FIS2_DbContext,FIS2ContextFactory>(options => options.UseSqlServer(Configuration.GetConnectionString("FIS2")));