2

I am making a messaging system with scheduled jobs to send sms messages and emails from queues. However, I cannot get the below code to work with multiple jobs, it works with a single job. I am also having an issue with the job erroring with "Cannot consume scoped service !!!the context!!! from singleton !!!the job!!!".

The below is my attempt at implementing multiple jobs.

QuartzServicesUtilities

 public static class QuartzServicesUtilities
   {
       public static void StartJob<TJob>(IScheduler scheduler, string cron)
            where TJob : IJob
    {
        var jobName = typeof(TJob).FullName;

        var job = JobBuilder.Create<TJob>()
            .WithIdentity(jobName)
            .Build();

        var trigger = TriggerBuilder.Create()
            .ForJob(job)
            .WithCronSchedule(cron, x => x.WithMisfireHandlingInstructionIgnoreMisfires())
            .WithIdentity($"{jobName}.trigger")
            .StartNow()
            .Build();

        scheduler.ScheduleJob(job, trigger);
    }
}

QuartzExtensions

public static class QuartzExtensions
{
    public static void UseQuartz(this IServiceCollection services, params Type[] jobs)
    {
        services.AddSingleton<IJobFactory, JobFactory>();
        services.Add(jobs.Select(jobType => new ServiceDescriptor(jobType, jobType, ServiceLifetime.Singleton)));

        services.AddSingleton(provider =>
        {
            var schedulerFactory = new StdSchedulerFactory();
            var scheduler = schedulerFactory.GetScheduler().Result;
            scheduler.JobFactory = provider.GetService<IJobFactory>();
            scheduler.Start();
            return scheduler;
        });
    }
}

JobFactory

public class JobFactory : IJobFactory
{
    private readonly IServiceProvider _serviceProvider;

    public JobFactory(IServiceProvider serviceProvider)
    {
        _serviceProvider = serviceProvider;
    }

    public IJob NewJob(TriggerFiredBundle bundle, IScheduler scheduler)
    {
        var jobDetail = bundle.JobDetail;
        var job = (IJob)_serviceProvider.GetService(jobDetail.JobType);

        return job;
    }

    public void ReturnJob(IJob job)
    {
        if (job is IDisposable disposable)
        {
            disposable.Dispose();
        }
    }
}

SMSJob

public class SMSJob : IJob
{
    private readonly IMessagingRepository _messagingRepository;
    private readonly ISMSEngine _smsEngine;

    public SMSJob(IMessagingRepository messagingRepository, ISMSEngine smsEngine)
    {
        _smsEngine = smsEngine;
        _messagingRepository = messagingRepository;
    }

    public async Task Execute(IJobExecutionContext context)
    {
       !!! the job !!!

        await Task.CompletedTask;
    }
}

EmailJob

public class EmailJob : IJob
{
    private readonly IEmailEngine _emailEngine;
    private readonly IMessagingRepository _messagingRepository;

    public EmailJob(IMessagingRepository messagingRepository, IEmailEngine emailEngine)
    {
        _emailEngine = emailEngine;
        _messagingRepository = messagingRepository;
    }

    public async Task Execute(IJobExecutionContext context)
    {

        !!! the other job !!!

        await Task.CompletedTask;
    }
}

Startup

  public class Startup
  {
      public Startup(IConfiguration configuration)
      {
          Configuration = configuration;
        }

      public IConfiguration Configuration { get; }


    public void Configure(IApplicationBuilder app, IHostingEnvironment env, IOptions<AppSettings> settings)
    {
        app.UseDeveloperExceptionPage();
        app.UseHttpsRedirection();
        app.UseStaticFiles();
        app.UseCookiePolicy();

        app.UseMvc(routes =>
        {
            routes.MapRoute(
                name: "default",
                template: "{controller=Home}/{action=Index}/{id?}");
        });

        var scheduler = app.ApplicationServices.GetService<IScheduler>();

        QuartzServicesUtilities.StartJob<EmailJob>(scheduler, settings.Value.EmailCronSchedule);
        QuartzServicesUtilities.StartJob<SMSJob>(scheduler, settings.Value.SMSCronSchedule);
    }


    public void ConfigureServices(IServiceCollection services)
    {
        services.Configure<CookiePolicyOptions>(options =>
        {
            // This lambda determines whether user consent for non-essential cookies is needed
            // for a given request.
            options.CheckConsentNeeded = context => true;
            options.MinimumSameSitePolicy = SameSiteMode.None;
        });

        services.Configure<AppSettings>(Configuration.GetSection("AppSettings"));

        #region Engines

        !!! engines !!!

        #endregion Engines

        #region Providers

        !!! providers !!!

        #endregion Providers

        #region Repositories

        !!! repos !!! i.e.
        services.AddTransient<IMessagingRepository, MessagingRepository>();

        #endregion Repositories

        services.AddDbContext<Context>();

        services.UseQuartz(typeof(EmailJob));
        services.UseQuartz(typeof(SMSJob));

        services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
    }
}
ajs117
  • 93
  • 1
  • 9
  • You're likely running into a "captive dependency" issue where a dependency has a shorter lifetime (e.g. Scoped) than the dependent class (e.g. Singleton). See this answer for a solution to that: https://stackoverflow.com/a/37511175/5803406 – devNull Feb 01 '19 at 12:19
  • Thank you, man, for your code. you really helped me to save my time! Love you! ))) – Dmitriy Jul 09 '19 at 14:54

0 Answers0