Cuarzo en ASP.NET Core

Introducción


Sé que hay muchos artículos y algún tipo de tutoriales sobre este tema, ni siquiera estoy hablando de documentación oficial, pero cuando trabajé en mi último proyecto me encontré con un problema muy interesante, que no se menciona mucho. Hoy hablaremos sobre el problema de usar la inyección de dependencia y el cuarzo en un proyecto ASP.NET Core.

Todo comenzó con el hecho de que no creía que pudiera surgir ningún problema y diría de inmediato que intenté usar varios enfoques: agregué todas las clases que Quartz incluyó en los servicios y las usé a través de DI - by (pero no completamente, resultó más tarde), intenté agregar HostedService; tampoco funcionó (al final adjuntaré algunos enlaces a artículos útiles sobre cómo trabajar con Quartz) y así sucesivamente. Ya pensé que tenía un problema con el gatillo, tampoco. En este breve artículo, intentaré ayudar a quienes hayan tenido el mismo problema y espero que mi solución los ayude en su trabajo futuro. Al final de la introducción, quiero agregar que estaré muy agradecido si en los comentarios aquellos que están familiarizados con la tecnología dan algunos consejos que ayudarán a mejorar lo que propuse.

Cuarzo


Cree un proyecto (o tome uno terminado, no importa) y agregue dos carpetas y varias clases:

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

En la interfaz IEmailSender, que servirá como ejemplo , cree un método para enviar cartas al correo:

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

Ahora describimos la clase que implementará esta interfaz:

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

Ahora describimos las clases DataJob.cs, DataScheduler.cs, JobFactory.cs. La clase DataJob implementará la interfaz 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")
            }
        }
    }

Como vemos un campo de tipo IServiceScopeFactory, obtendremos servicios directamente desde Startup. Fue este enfoque el que me ayudó a resolver mi problema, seguir adelante y describir la clase DataScheduler en la que agregaremos trabajo y activaremos en el Sheduler del cuarzo mismo:

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

Y ahora la clase JobFactory, que implementa la interfaz 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
        }
    }

Como puede ver, yo, de hecho, obtengo todas las dependencias directamente de serviceScopeFactory. Todo está casi listo, queda por cambiar la clase del Programa:

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

Y agregue lo siguiente a Inicio en el método ConfigureServices:

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

Hecho. Ahora, cuando inicia la aplicación, creamos una tarea que se activará cada minuto. El valor se puede cambiar en DataScheduler.Start (también se puede especificar en segundos, horas o usar CRON). Para cada nueva tarea con este enfoque, debe crear una nueva clase que implemente IJob y registre una nueva tarea DataScheduler. También puede crear una clase de Programador separada para una nueva tarea.

Me

encantaría poder ayudar a alguien, pero aquí hay un par de artículos útiles sobre Quartz y su uso: Creación de un servicio alojado Quartz.NET con ASP.NET Core
Uso de servicios con alcance dentro de un servicio alojado Quartz.NET con ASP.NET Core

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


All Articles