在 Blazor 服务器中使用数据库上下文工厂

Using a DB context factory in Blazor server

我在为我的服务器端 Blazor 应用程序设置 EF 数据库连接时遇到了一些问题。它使用标准 DbContext 设置,直到我注意到由于 Blazor 始终使用相同上下文的性质,连接无法正确关闭的一些问题。我的研究让我查看了 DbContextFactory,但现在已弃用接口 IDbContextFactory,取而代之的是 IDesignTimeDbContextFactory

我设置了一个class来实现接口:

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);
    }
}

我要使用的DbContext是这个,它继承并扩展了EF Power Tools生成的DbContext:

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; }
}

在我的 startup.cs 中,我在 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>();
    }

我的问题是,在设置使用此数据库连接的服务时,我在 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'.)

作为参考,这里有一个如何设置TimetableService的例子(其他的以同样的方式实例化):

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;
        }
    }
}

是因为我在启动时使用了错误的方法来创建上下文工厂,还是后来我弄错了?

如果你想解析一个特定的工厂类型,你必须注册这个重载,AddDbContextFactory<TContext,TFactory> 记录 here:

This overload allows a specific implementation of IDbContextFactory to be registered instead of using the default factory shipped with EF Core.

所以

services.AddDbContextFactory<FIS2_DbContext,FIS2ContextFactory>(options => options.UseSqlServer(Configuration.GetConnectionString("FIS2")));