Quartz dans ASP.NET Core

introduction


Je sais qu'il y a beaucoup d'articles et une sorte de tutoriels sur ce sujet, je ne parle même pas de documentation officielle, mais en travaillant sur mon dernier projet, je suis tombé sur un problème très intéressant, qui n'est pas beaucoup mentionné. Aujourd'hui, nous allons parler du problème de l'utilisation de l'injection de dépendances et du quartz dans un projet ASP.NET Core.

Tout a commencé par le fait que je ne pensais pas que des problèmes pouvaient survenir et je dirais tout de suite que j'ai essayé d'utiliser différentes approches: j'ai ajouté toutes les classes que Quartz incluait dans les services et les ai utilisées via DI - par (mais pas complètement, comme il s'est avéré plus tard), j'ai essayé d'ajouter HostedService - cela n'a pas fonctionné non plus (à la fin, je vais joindre de bons liens vers des articles utiles sur le travail avec Quartz) et ainsi de suite. Je pensais déjà que j'avais un problème avec la gâchette - ni l'un ni l'autre. Dans ce court article, je vais essayer d'aider ceux qui ont peut-être eu le même problème et j'espère que ma solution les aidera dans leur travail futur. À la fin de l'introduction, je tiens à ajouter que je serai très reconnaissant si, dans les commentaires, ceux qui connaissent bien la technologie donnent des conseils qui permettront d'améliorer ce que j'ai proposé.

Quartz


Créez un projet (ou prenez-en un terminé - peu importe) et ajoutez-y deux dossiers et plusieurs classes:

Quartz
--DataJob.cs
--DataScheduler.cs
--JobFactory.cs
Workers
--EmailSender
--IEmailSender

Dans l'interface IEmailSender, qui servira d'exemple , créez une méthode pour envoyer des lettres par courrier:

public interface IEmailSender
    {
        Task SendEmailAsync(string email, string subject, string message);
    }

Nous décrivons maintenant la classe qui implémentera cette interface:

public class EmailSender : IEmailSender
    {
        
        public Task SendEmailAsync(string email, string subject, string message)
		{
			var from = "****@gmail.com";
			var pass = "****";
            SmtpClient client = new SmtpClient("smtp.gmail.com", 587);
			client.DeliveryMethod = SmtpDeliveryMethod.Network;
			client.UseDefaultCredentials = false;
			client.Credentials = new System.Net.NetworkCredential(from, pass);
			client.EnableSsl = true;
			var mail = new MailMessage(from, email);
			mail.Subject = subject;
			mail.Body = message;
			mail.IsBodyHtml = true;
			return client.SendMailAsync(mail);
		}
    }

Nous décrivons maintenant les classes DataJob.cs, DataScheduler.cs, JobFactory.cs. La classe DataJob implémentera l'interface IJob.

 public class DataJob : IJob
    {
        private readonly IServiceScopeFactory serviceScopeFactory;

        public DataJob(IServiceScopeFactory serviceScopeFactory)
        {
            this.serviceScopeFactory = serviceScopeFactory;
        }

        public async Task Execute(IJobExecutionContext context)
        {
            using (var scope = serviceScopeFactory.CreateScope())
            {
                var emailsender = scope.ServiceProvider.GetService<IEmailSender>();
                
                 await emailsender.SendEmailAsync("example@gmail.com","example","hello")
            }
        }
    }

Comme nous voyons un champ de type IServiceScopeFactory, nous obtiendrons des services directement à partir du démarrage. C'est cette approche qui m'a aidé à résoudre mon problème, allez-y et décrivez la classe DataScheduler dans laquelle nous allons ajouter du travail et déclencher dans le Sheduler du quartz lui-même:

public static class DataScheduler
    {
        
        public static async void Start(IServiceProvider serviceProvider)
        {
            IScheduler scheduler = await StdSchedulerFactory.GetDefaultScheduler();
            scheduler.JobFactory = serviceProvider.GetService<JobFactory>();
            await scheduler.Start();

            IJobDetail jobDetail = JobBuilder.Create<DataJob>().Build();
            ITrigger trigger = TriggerBuilder.Create()
                .WithIdentity("MailingTrigger", "default")
                .StartNow()
                .WithSimpleSchedule(x => x
                .WithIntervalInMinutes(1)
                .RepeatForever())
                .Build();

            await scheduler.ScheduleJob(jobDetail, trigger);
        }

Et maintenant la classe JobFactory, qui implémente l'interface IJobFactory:

 public class JobFactory : IJobFactory
    {
        protected readonly IServiceScopeFactory serviceScopeFactory;

        
        public JobFactory(IServiceScopeFactory serviceScopeFactory)
        {
            this.serviceScopeFactory = serviceScopeFactory;
        }

        public IJob NewJob(TriggerFiredBundle bundle, IScheduler scheduler)
        {
            using (var scope = serviceScopeFactory.CreateScope())
            {
                var job = scope.ServiceProvider.GetService(bundle.JobDetail.JobType) as IJob;
                return job;
            }
            
        }

        public void ReturnJob(IJob job)
        {
           //Do something if need
        }
    }

Comme vous pouvez le voir, j'obtiens en fait toutes les dépendances directement de serviceScopeFactory. Tout est presque prêt, il reste à changer la classe Program:

public class Program
    {
        public static void Main(string[] args)
        {
            var host = BuildWebHost(args);
            using (var scope = host.Services.CreateScope())
            {
                var serviceProvider = scope.ServiceProvider;
                try
                {
                    DataScheduler.Start(serviceProvider);
                }
                catch (Exception)
                {
                    throw;
                }
            }
            host.Run();
        }

        public static IWebHost BuildWebHost(string[] args) =>
           WebHost.CreateDefaultBuilder(args)
               .UseStartup<Startup>()
               .Build();
    }

Et ajoutez ce qui suit au démarrage dans la méthode ConfigureServices:

   services.AddTransient<JobFactory>();
   services.AddScoped<DataJob>();
   services.AddScoped<IEmailSender,EmailSender>();

Terminé. Maintenant, lorsque vous démarrez l'application, nous créons une tâche qui se déclenchera toutes les minutes. La valeur peut être modifiée dans DataScheduler.Start (peut également être spécifiée en secondes, heures ou utiliser CRON). Pour chaque nouvelle tâche avec cette approche, vous devez créer une nouvelle classe qui implémentera IJob et enregistrera une nouvelle tâche DataScheduler. Vous pouvez également créer une classe Scheduler distincte pour une nouvelle tâche.

Je serais très heureux si je pouvais aider quelqu'un, mais voici quelques articles utiles sur Quartz et son utilisation:

Création d'un service hébergé Quartz.NET avec ASP.NET Core
Utilisation de services étendus dans un service hébergé Quartz.NET avec ASP.NET Core

Source: https://habr.com/ru/post/undefined/


All Articles