Asp.net Core Quartz 作业调用控制器方法,但上下文

Posted

技术标签:

【中文标题】Asp.net Core Quartz 作业调用控制器方法,但上下文【英文标题】:Asp.net Core Quartz job calling controller method, but context 【发布时间】:2021-06-15 22:05:50 【问题描述】:

我有一个 aspnetcore 服务器(服务于 Blazor wasm 应用程序),它有一个 Quartz 计划作业正在运行。当作业触发时,它会调用我的服务器控制器之一上的方法。

如果我在我的控制器上正常调用此方法(例如通过 Web API 调用),它工作正常。

当我从 Quartz IJob 调用该方法时,控制器中使用的 DbContext 似乎已被释放。 我尝试过以正常方式将控制器注入到作业中,也可以通过 IServiceProvider 注入,两者的结果相同。

控制器:

public class NotificationController : ControllerBase

    private readonly ApplicationDbContext context;
    
    public NotificationService(ApplicationDbContext context)
    
        this.context = context;
    

    public async Task MyMethod()
    
        await context.SaveChangesAsync(); //This is where it fails when Quartz calls it, seems context is not populated
    

我的工作(IServiceProvider 尝试):

public class ReminderJob : IJob

    private readonly IServiceProvider serviceProvider;

    public ReminderJob(IServiceProvider serviceProvider)
    
        this.serviceProvider = serviceProvider;
    

    public async Task Execute(IJobExecutionContext jobcontext)
    
        using (var scope = serviceProvider.CreateScope())
        
            await scope.ServiceProvider.GetRequiredService<NotificationController>().MyMethod();
        
    

我的工作(DI 尝试):

public class ReminderJob : IJob

    private readonly NotificationController notificationController;

    public ReminderJob(NotificationController notificationController)
    
        this.notificationController = notificationController;
    

    public async Task Execute(IJobExecutionContext jobcontext)
    
        await notificationController.MyMethod();
    

我的 Startup.cs(ConfigureServices 中的相关行):

public void ConfigureServices(IServiceCollection services)
    
        services.AddSignalR();
        services.AddControllersWithViews();
        services.AddMvcCore().AddControllersAsServices();
        services.AddRazorPages();

        services.AddQuartz(q =>
        
            q.UseMicrosoftDependencyInjectionScopedJobFactory(); //I also tried passing options.CreateScope to this method, but no difference
            q.AddJobAndTrigger<ReminderJob>(configuration);
        );
        services.AddQuartzHostedService(q => q.WaitForJobsToComplete = true);

        services.AddDbContext<ApplicationDbContext>(options => 
               options.UseSqlServer(configuration.GetConnectionString("DefaultConnection")));

VS 尝试执行 context.SaveChangesAsync() 时不会引发异常,但是,在它没有命中后直接一个断点,但是当我在调试时检查上下文的详细信息时,它似乎没有被填充正确。

如何在 IJob 中使用 Controller,并确保不释放 Controller 的依赖项?

【问题讨论】:

【参考方案1】:

如何在 IJob 中使用控制器

不要在作业中使用控制器。

将所需的功能移动/提取/重构到服务抽象中

//Service abstraction
public interface INotificationService 
    Task MyMethod();


public class NotificationService : INotificationService 
    private readonly ApplicationDbContext context;

    public NotificationService(ApplicationDbContext context) 
        this.context = context;
    

    public async Task MyMethod() 
        await context.SaveChangesAsync();
    

并让作业和控制器依赖于服务来调用所需的功能。

public class NotificationController : ControllerBase 
    private readonly INotificationService service;
    
    public NotificationController (INotificationService service ) 
        this.service = service ;
        

    public async Task<IActionResult> MyMethod() 
        await service.MyMethod(); 
        return Ok();
    


public class ReminderJob : IJob 
    private readonly INotificationService service;

    public ReminderJob(INotificationService service) 
        this.service = service;
    

    public Task Execute(IJobExecutionContext jobcontext) 
        return service.MyMethod();
    

当然还有向 DI 容器注册所有必要的服务。

//...

services.AddScoped<INotificationService, NotificationService>();

//...

【讨论】:

以上是关于Asp.net Core Quartz 作业调用控制器方法,但上下文的主要内容,如果未能解决你的问题,请参考以下文章

如何强制 Quartz 现在在 ASP.NET Core 中启动?

在 ASP.NET Core 应用程序中禁用 Quartz.Net 的调试日志记录

ASP.NET Core2.2+Quartz.Net 实现web定时任务

ASP.NET MVC5+EF6+EasyUI 后台管理系统(84)-Quartz 作业调度用法详解一

Quartz.NET开源作业调度架构

Quartz.net