Home > front end >  Using a DB context factory in Blazor server
Using a DB context factory in Blazor server

Time:12-01

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")));
  • Related