Trigger Quartz.net job manually from controller
Asked Answered
S

1

13

I've built a sample .NET 5 Web API application in VS2019, I had the default template (with WeatherForecastController). I've added Quartz.net (version 3.3.3) to project and in Startup class I've added:

public void ConfigureServices(IServiceCollection services)
{
    services.AddQuartz(q =>
    {
        q.UseMicrosoftDependencyInjectionJobFactory();
        q.AddJobAndTrigger<HelloWorldJob>(Configuration);
    });

    // ASP.NET Core hosting
    services.AddQuartzServer(options =>
    {
        // when shutting down we want jobs to complete gracefully
        options.WaitForJobsToComplete = true;
    });

    services.AddControllers();
    services.AddSwaggerGen(c =>
    {
        c.SwaggerDoc("v1", new OpenApiInfo { Title = "QuartzTest", Version = "v1" });
    });
}

and created a sample job.
The code for AddJobAndTrigger is taken from https://andrewlock.net/using-quartz-net-with-asp-net-core-and-worker-services/

That part is working perfectly, but now I'd like to give the end-user the ability to trigger a specific job manually via rest API.

I've created a simple controller that should be able to trigger jobs manually by using:

scheduler.TriggerJob(new Jobkey("MarkAsSolutionReminderJob"));

(ref: Quartz.Net Trigger Scheduled Job On Demand)

Below is my controller:

[ApiController]
[Route("[controller]")]
public class QuartzController : ControllerBase
{
    private readonly IScheduler _scheduler;
    private readonly ILogger<WeatherForecastController> _logger;

    public QuartzController(IScheduler scheduler, ILogger<WeatherForecastController> logger)
    {
        _scheduler = scheduler;
        _logger = logger;
    }

    [HttpGet("Run")]
    public async Task<OkObjectResult> Run(string jobName)
    {
        //check if a specific job exists

        //if yes then run that job
        await _scheduler.TriggerJob(new JobKey("HelloWorldJob"));
        return Ok("OK");
    }
}

but when I try to run it I get this error:

System.InvalidOperationException: Unable to resolve service for type 'Quartz.IScheduler' while attempting to activate 'QuartzTest.Controllers.QuartzController'. at Microsoft.Extensions.DependencyInjection.ActivatorUtilities.GetService(IServiceProvider sp, Type type, Type requiredBy, Boolean isDefaultParameterRequired)
at lambda_method10(Closure , IServiceProvider , Object[] ) at Microsoft.AspNetCore.Mvc.Controllers.ControllerActivatorProvider.<>c__DisplayClass4_0.b__0(ControllerContext controllerContext) at Microsoft.AspNetCore.Mvc.Controllers.ControllerFactoryProvider.<>c__DisplayClass5_0.g__CreateController|0(ControllerContext controllerContext) at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted) at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.InvokeInnerFilterAsync() --- End of stack trace from previous location --- at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.g__Awaited|19_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted) at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.g__Awaited|17_0(ResourceInvoker invoker, Task task, IDisposable scope) at Microsoft.AspNetCore.Routing.EndpointMiddleware.g__AwaitRequestTask|6_0(Endpoint endpoint, Task requestTask, ILogger logger) at Microsoft.AspNetCore.Authorization.AuthorizationMiddleware.Invoke(HttpContext context) at Swashbuckle.AspNetCore.SwaggerUI.SwaggerUIMiddleware.Invoke(HttpContext httpContext) at Swashbuckle.AspNetCore.Swagger.SwaggerMiddleware.Invoke(HttpContext httpContext, ISwaggerProvider swaggerProvider) at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.Invoke(HttpContext context)

How can I access the scheduler inside the controllers and ideally check if a specific job exists and what is their status (is it running)?

Shah answered 19/11, 2021 at 13:35 Comment(0)
L
28

I thought that services.AddQuartz will be enough.

AddQuartz adds the scheduler factory, not the IScheduler to the DI container.

Since getting the scheduler is done async, it would be difficult to set it up during startup.

refactor your code to instead depend on the factory and use that to get the scheduler

[ApiController]
[Route("[controller]")]
public class QuartzController : ControllerBase {
    private readonly ISchedulerFactory factory;
    private readonly ILogger<WeatherForecastController> _logger;

    public QuartzController(ISchedulerFactory factory, ILogger<WeatherForecastController> logger) {
        this.factory = factory;
        _logger = logger;
    }

    [HttpGet("Run")]
    public async Task<OkObjectResult> Run(string jobName) {
        IScheduler scheduler = await factory.GetScheduler();

        //...check if a specific job exists

        //if yes then run that job
        await scheduler.TriggerJob(new JobKey("HelloWorldJob"));

        return Ok("OK");
    }
}
Lunular answered 19/11, 2021 at 15:49 Comment(2)
I just found an example in the repo: github.com/quartznet/quartznet/blob/main/src/…. Doh, I must look at the project repo next time. Thank you for your help!Shah
I wonder if running Quartz.NET from an ASP.NET MVC is possible. This scheduled task will take some parameters and the user can start/stop it from this web application. Any sample that I can follow?Agonize

© 2022 - 2024 — McMap. All rights reserved.