Quarz in ASP.NET Core

Einführung


Ich weiß, dass es zu diesem Thema viele Artikel und Tutorials gibt. Ich spreche nicht einmal über offizielle Dokumentation, aber bei der Arbeit an meinem neuesten Projekt bin ich auf ein sehr interessantes Problem gestoßen, das nicht sehr oft erwähnt wird. Heute werden wir über das Problem der Verwendung von Dependency Injection und Quartz in einem ASP.NET Core-Projekt sprechen.

Alles begann mit der Tatsache, dass ich nicht dachte, dass es irgendwelche Probleme geben könnte, und ich werde sofort sagen, dass ich versucht habe, verschiedene Ansätze zu verwenden: Ich habe alle Klassen hinzugefügt, die Quartz in Services enthalten hat, und sie über DI verwendet - by (aber nicht vollständig) Wie sich später herausstellte, habe ich versucht, HostedService hinzuzufügen - es hat auch nicht funktioniert (am Ende werde ich einige gute Links zu nützlichen Artikeln über die Arbeit mit Quarz anhängen) und so weiter. Ich dachte schon, dass ich ein Problem mit dem Abzug habe - auch nicht. In diesem kurzen Artikel werde ich versuchen, denen zu helfen, die möglicherweise das gleiche Problem hatten, und hoffe, dass meine Lösung ihnen bei ihrer zukünftigen Arbeit hilft. Am Ende der Einführung möchte ich hinzufügen, dass ich sehr dankbar sein werde, wenn in den Kommentaren diejenigen, die mit der Technologie gut vertraut sind, einige Ratschläge geben, die dazu beitragen, meine Vorschläge zu verbessern.

Quarz


Erstellen Sie ein Projekt (oder nehmen Sie ein fertiges - es spielt keine Rolle) und fügen Sie zwei Ordner und mehrere Klassen hinzu:

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

In der IEmailSender-Oberfläche, die als Beispiel dient Erstellen Sie eine Methode zum Senden von Briefen an E-Mails:

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

Nun beschreiben wir die Klasse, die diese Schnittstelle implementiert:

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);
		}
    }

Nun beschreiben wir die Klassen DataJob.cs, DataScheduler.cs, JobFactory.cs. Die DataJob-Klasse implementiert die IJob-Schnittstelle.

 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")
            }
        }
    }

Da wir ein Feld vom Typ IServiceScopeFactory sehen, erhalten wir Dienste direkt vom Start. Dieser Ansatz hat mir geholfen, mein Problem zu lösen, die DataScheduler-Klasse zu beschreiben, in der wir im Sheduler des Quarzes selbst Job hinzufügen und auslösen werden:

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);
        }

Und jetzt die JobFactory-Klasse, die die IJobFactory-Schnittstelle implementiert:

 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
        }
    }

Wie Sie sehen können, erhalte ich tatsächlich alle Abhängigkeiten direkt von serviceScopeFactory. Alles ist fast fertig, es bleibt die Programmklasse zu ändern:

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();
    }

Fügen Sie in der ConfigureServices-Methode Folgendes zum Start hinzu:

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

Erledigt. Wenn Sie jetzt die Anwendung starten, erstellen wir eine Aufgabe, die jede Minute ausgelöst wird. Der Wert kann in DataScheduler.Start geändert werden (kann auch in Sekunden, Stunden oder mit CRON angegeben werden). Für jede neue Aufgabe mit diesem Ansatz müssen Sie eine neue Klasse erstellen, die IJob implementiert und eine neue DataScheduler-Aufgabe registriert. Sie können auch eine separate Scheduler-Klasse für eine neue Aufgabe erstellen.

Ich würde mich sehr freuen, wenn ich jemandem helfen könnte, aber hier sind einige nützliche Artikel über Quartz und seine Verwendung:

Erstellen eines von Quartz.NET gehosteten Dienstes mit ASP.NET Core Verwenden von Diensten mit
Gültigkeitsbereich in einem von Quartz.NET gehosteten Dienst mit ASP.NET Core

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


All Articles