Featured

ASP.NET 6 Scheduling with Quartz.NET and Signalr Monitoring

Last modified: March 20, 2022

In this article, we are going to use Quartz.NET to schedule our jobs and make use of Signalr to view monitoring the result/progress.

1. Create Project

Create an ASP.NET MVC application.

createproject

createproject

createproject

createproject

2. Install NuGet Package

  • Microsoft.AspNetCore.SignalR.Client
  • Microsoft.Extensions.Hosting
  • Quartz
  • Quartz.Extensions.Hosting

NuGet

NuGet

NuGet

NuGet

3. Install SignalR client library

@microsoft/[email protected]

client

4. Create SignalR js file

create a hub.js file and save to wwwroot->js folder

const connection = new signalR.HubConnectionBuilder().withUrl("/hub").build(); async function start() { try { await connection.start(); console.log("SignalR Connected."); } catch (err) { console.log(err); setTimeout(start, 5000); } }; connection.onclose(async () => { await start(); }); start(); connection.on("ConcurrentJobs", function (message) { var li = document.createElement("li"); document.getElementById("concurrentJobs").appendChild(li); li.textContent = `${message}`; }); connection.on("NonConcurrentJobs", function (message) { var li = document.createElement("li"); document.getElementById("nonConcurrentJobs").appendChild(li); li.textContent = `${message}`; });

5. HTML to display message

In the Index.cshtml, add below code to display messsage from SignalR

<div class="container"> <div class="row"> <div class="col-6"> <ul id="concurrentJobs"></ul> </div> <div class="col-6"> <ul id="nonConcurrentJobs"></ul> </div> </div> </div> <script src="~/microsoft/Signalr/dist/browser/signalr.js"></script> <script src=~/js/hub.js></script>

NuGet

7. Job Hub

Create a cs class file name JobsHub and replace with below code

public class JobsHub : Hub { //message used for concurrent job message public Task SendConcurrentJobsMessage(string message) { return Clients.All.SendAsync("ConcurrentJobs", message); } //message used for nonconcurrent job message public Task SendNonConcurrentJobsMessage(string message) { return Clients.All.SendAsync("NonConcurrentJobs", message); } }

8. Create Jobs

  • ConconcurrentJob

Create a cs class file named ConconcurrentJob and replace with below code.

public class ConconcurrentJob : IJob { private static int _counter = 0; private readonly IHubContext<JobsHub> _hubContext; public ConconcurrentJob(IHubContext<JobsHub> hubContext) { _hubContext = hubContext; } public async Task Execute(IJobExecutionContext context) { var count = _counter++; //start the job var beginMessage = $"Conconcurrent Job BEGIN {count} {DateTime.UtcNow}"; await _hubContext.Clients.All.SendAsync("ConcurrentJobs", beginMessage); //we have long running job Thread.Sleep(5000); //complete the job var endMessage = $"Conconcurrent Job END {count} {DateTime.UtcNow}"; await _hubContext.Clients.All.SendAsync("ConcurrentJobs", endMessage); } }

Create a cs class file named NonConconcurrentJob and replace with below code.

public class ConconcurrentJob : IJob { private static int _counter = 0; private readonly IHubContext<JobsHub> _hubContext; public ConconcurrentJob(IHubContext<JobsHub> hubContext) { _hubContext = hubContext; } public async Task Execute(IJobExecutionContext context) { var count = _counter++; //start the job var beginMessage = $"Conconcurrent Job BEGIN {count} {DateTime.UtcNow}"; await _hubContext.Clients.All.SendAsync("nonConcurrentJobs", beginMessage); //we have long running job Thread.Sleep(5000); //complete the job var endMessage = $"Conconcurrent Job END {count} {DateTime.UtcNow}"; await _hubContext.Clients.All.SendAsync("nonConcurrentJobs", endMessage); } }

9. Program.cs

Add below code to Program.cs

builder.Services.AddQuartz(q => { q.UseMicrosoftDependencyInjectionJobFactory(); var conconcurrentJobKey = new JobKey("ConconcurrentJob"); q.AddJob<ConconcurrentJob>(opts => opts.WithIdentity(conconcurrentJobKey)); q.AddTrigger(opts => opts .ForJob(conconcurrentJobKey) .WithIdentity("ConconcurrentJob-trigger") .WithSimpleSchedule(x => x .WithIntervalInSeconds(5) .RepeatForever())); var nonConconcurrentJobKey = new JobKey("NonConconcurrentJob"); q.AddJob<NonConconcurrentJob>(opts => opts.WithIdentity(nonConconcurrentJobKey)); q.AddTrigger(opts => opts .ForJob(nonConconcurrentJobKey) .WithIdentity("NonConconcurrentJob-trigger") .WithSimpleSchedule(x => x .WithIntervalInSeconds(5) .RepeatForever())); }); builder.Services.AddQuartzHostedService( q => q.WaitForJobsToComplete = true); builder.Services.AddSignalR();
app.UseEndpoints(endpoints => { endpoints.MapHub<JobsHub>("/hub"); });

10. Test

test