Task scheduling web management platform based on ASP. Net core 5.0 and relying on quartz. Net framework

Time:2021-8-18

Source address:  https://github.com/246850/Calamus.TaskScheduler

Demo address:http://47.101.47.193:1063/

 

 

1. Quartz.net framework core classes

Ischeduler: dispatcher

Ijobdetail: task

Itrigger: trigger

Jobkey: task / trigger ID

Jobdatamap: packet

2. Email notification (fluent email Class Library)

_fluentEmail.To(to).Subject(subject).Body(body, true).SendAsync();

3. Quartz.net is hosted by ihostedservice background service

internal class QuartzHostedService : IHostedService
{
	private readonly ISchedulerFactory schedulerFactory;
	private readonly IOptions options;
	private IScheduler scheduler = null!;

	public QuartzHostedService(
		ISchedulerFactory schedulerFactory,
		IOptions options)
	{
		this.schedulerFactory = schedulerFactory;
		this.options = options;
	}

	public async Task StartAsync(CancellationToken cancellationToken)
	{
		scheduler = await schedulerFactory.GetScheduler(cancellationToken);
		await scheduler.Start(cancellationToken);
	}

	public Task StopAsync(CancellationToken cancellationToken)
	{
		return scheduler.Shutdown(options.Value.WaitForJobsToComplete, cancellationToken);
	}
}

4.Asp.Net   Core 5.0 integration

Install dependent packages

Quartz

Quartz.AspNetCore

Quartz.Plugins.TimeZoneConverter

Quartz.Serialization.Json

FluentEmail.Core

FluentEmail.Smtp

Startup

public void ConfigureServices(IServiceCollection services)
{
	services.AddControllersWithViews(options =>
		{
			options.Filters.Add();    //  General execution result packaging processing filter
			options.Filters.Add();  //  Global exception filter
		})
		.AddJsonOptions(options =>
		{
			options.JsonSerializerOptions.Converters.Add(new DateTimeConverter());  //  Date formatting
		})
		. addfluentvalidation (config = > // request model parameter validation
		{
			config.RunDefaultMvcValidationAfterFluentValidationExecutes = true;    //  False: disable default model validation
			config.ValidatorOptions.CascadeMode = CascadeMode.Stop; //  Without cascading validation, the first rule error stops
			config.RegisterValidatorsFromAssemblyContaining();
		});
	services.AddHostedService();   //  NLog shutdown service
	services.AddDistributedMemoryCache();  //  Distributed cache interface
	services.AddSingleton(HtmlEncoder.Create(UnicodeRanges.All));//  Solve Chinese garbled code
	services.AddHttpClient();   // IHttpClientFactory

	IConfigurationSection quartzConfiguration = Configuration.GetSection("Quartz"); //  Quartz configuration node

	/***********Quartz.NET*********/
	services.AddTransient();   //  To register a job to a container, you must step
	services.AddQuartz(config =>
	{
		config.UseTimeZoneConverter();
		//Create a job instance from a container using the Microsoft dependencyinjectionjobfactory factory class
		config.UseMicrosoftDependencyInjectionJobFactory(options =>
		{
			options.AllowDefaultConstructor = false;    //  It is forbidden to create a job with a parameterless build function
			options.CreateScope = false;
		});
		config.UseDefaultThreadPool(options =>
		{
			options.MaxConcurrency = 10;    //  Maximum concurrent execution threads
		});
		config.UsePersistentStore(options =>
		{
			options.UseProperties = false;
			//options.UseBinarySerializer();  //  Binary Serialization 
			options.UseJsonSerializer();    //  JSON serialization
			options.UseMySql(ado =>
			{
				ado.ConnectionString = quartzConfiguration["Database"];
				ado.TablePrefix = quartzConfiguration["TablePrefix"];  //  Default qrtz_
				ado.ConnectionStringName = "Quartz.net";
			});
		});

		//Listener
		config.AddSchedulerListener();
		config.AddJobListener();
		config.AddTriggerListener();

		//Start NLog log file cleanup job
		config.ScheduleJob(trigger =>
		{
			trigger.WithIdentity(NLogJobKey.NameKey, NLogJobKey.GroupKey).StartNow()
			.WithCronSchedule("0 0 0 1/3 * ? ",  cron => cron.WithMisfireHandlingInstructionFireAndProceed());    //  Starting from the 1st day of each month, it shall be implemented every 3 days
		}, job =>
		{
			job.WithIdentity(NLogJobKey.NameKey, NLogJobKey.GroupKey)
			. storedarably (false) // whether to persist and remove when there is no associated trigger. False: remove
			. requestrecovery() // whether to recover the task after restart
			. withdescription ("empty NLog log files every 3 days");
		});
	});
	//Ihostedservice host starts quartz services. Addsingleton()
	services.AddQuartzServer(options =>
	{
		// when shutting down we want jobs to complete gracefully
		options.WaitForJobsToComplete = true;   //  Wait until the task is completed before exiting
	});

	/***********FluentEmail*********/
	//In order to configure mail notification on job data, the built-in service registration method is not used
	//Services.addfluent email (quartzconfiguration ["SMTP: username"], "quartz.net task scheduling notification")
	//    .AddRazorRenderer()
	//    .AddSmtpSender(quartzConfiguration["Smtp:Host"], Convert.ToInt32(quartzConfiguration["Smtp:Port"]), quartzConfiguration["Smtp:UserName"], quartzConfiguration["Smtp:Password"]);
	services.AddTransient(serviceProvider =>
	{
		IScheduler scheduler = serviceProvider.GetRequiredService().GetScheduler().Result;

		JobKey key = new JobKey(EmailJobKeys.NameKey, EmailJobKeys.GroupKey);
		if (!scheduler.CheckExists(key).Result)
		{
			JobDataMap dataMap = new JobDataMap();
			dataMap.Put(EmailJobKeys.Host, "smtp.qq.com");
			dataMap.Put(EmailJobKeys.Port, 587);    //  Port 465 has been trying to fail. Strange
			dataMap.Put(EmailJobKeys.UserName, " [email protected] "); //  Author QQ, welcome harassment
			dataMap.Put(EmailJobKeys.Password, "cirxjtemuzxycagf");
			dataMap.Put(EmailJobKeys.To, string.Empty); //  Multiple recipients are supported for mail; separate
			Datamap.put (emailjobkeys.nickname, "quartz.net task scheduling notification");
			dataMap.Put(EmailJobKeys.CacheExpiry, 30);  //  The default is to notify only once in 30 minutes
			IJobDetail job = JobBuilder.Create()
				.StoreDurably(true)
				.RequestRecovery()
				. withdescription ("mail notification configuration job, do not delete")
				.WithIdentity(key)
				.UsingJobData(dataMap)
				.Build();
			scheduler.AddJob(job, true);   //  Initialize mail notification configuration
		}

		IJobDetail emailJob = scheduler.GetJobDetail(key).Result;
		IFluentEmail fluentEmail = new Email(new ReplaceRenderer(),
			new SmtpSender(new SmtpClient(emailJob.JobDataMap.GetString(EmailJobKeys.Host), emailJob.JobDataMap.GetInt(EmailJobKeys.Port))
			{
				EnableSsl = true,
				Credentials = new NetworkCredential(emailJob.JobDataMap.GetString(EmailJobKeys.UserName),
				emailJob.JobDataMap.GetString(EmailJobKeys.Password))
			}),
			emailJob.JobDataMap.GetString(EmailJobKeys.UserName),
			emailJob.JobDataMap.GetString(EmailJobKeys.NickName));
		return fluentEmail;
	});

	IocEngine.Instance.Init(services);  //  There's really no way to get a static container to get the service. The listener can't inject ischedulerfactory and ifluentemail through the constructor. I guess it should be a circular reference
}

  

 

5. Thanks for watching. Bye