Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Error accessing a service that uses DbContext on my quartz job

I am making a web API using ASP.NET Core and now I am having a problem with quartz scheduled jobs. The jobs I have will access my services to update the database. After some researches, I figured how to do the dependency injection so that my jobs can access the services, here is how I overrode the job factory:

public class AspNetCoreJobFactory : SimpleJobFactory
{
    IServiceProvider _provider;

    public AspNetCoreJobFactory(IServiceProvider provider)
    {
        _provider = provider;
    }

    public override IJob NewJob(TriggerFiredBundle bundle, IScheduler scheduler)
    {
        try
        {
            return (IJob)this._provider.GetService(bundle.JobDetail.JobType);
        }
        catch(Exception e)
        {
            throw new SchedulerException(string.Format("Problem while instantiating job '{0}' from the AspNet Core IOC.", bundle.JobDetail.Key), e);
        }
    }
}

and I added this line on my startup configure:

_quartzScheduler.JobFactory = new AspNetCoreJobFactory(app.ApplicationServices);

Lastly I added those two lines on my ConfigureServices method:

services.AddSingleton<IUserService, UserService>();
services.AddTransient<BatchJobCheckContract>();

right now I am getting this exception when trying to execute the job, it seems like it's because my service uses the DbContext, how can I solve this?

Cannot consume scoped service 'RHP.data.RHPDbContext' from singleton 'RHP.data.IServices.Administration.IUserService'.

like image 583
YOUSFI Mohamed Walid Avatar asked Oct 20 '25 03:10

YOUSFI Mohamed Walid


2 Answers

After playing around with Quartz (version 3.2.3), it looks like you do not have to write your own JobFactory to use Microsoft DI. (See ASP.NET Core Integration and Microsoft DI Integration):

Add the Quartz.AspNetCore nuget package and you can scoped services like this:

public void ConfigureServices(IServiceCollection services)
{
    services.AddScoped<IScopedService, ScopedService>();

    // Job has scoped dependencies, so it must be scoped as well
    services.AddScoped<Job>();

    services.AddQuartz(q =>
    {
        q.UseMicrosoftDependencyInjectionScopedJobFactory();

        var jobKey = new JobKey("job");
        q.AddJob<Job>(jobKey);

        q.AddTrigger(t => /* ... */));
    });

    services.AddQuartzServer(opts => opts.WaitForJobsToComplete = true);
}

However, if you cannot use the current version of Quartz.AspNetCore, you could still use IServiceProvider as dependency in your Job class and resolve services there:

public class Job : IJob
{
    private readonly IServiceProvider _serviceProvider;

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

    public Task Execute(IJobExecutionContext context)
    {
        using var scope = _serviceProvider.CreateScope();
        var scopedService = scope.ServiceProvider.GetRequiredService<IScopedService>();

        // ...
    }
}

Like this the Job class controls the lifetime of scoped services.

like image 190
Christian Held Avatar answered Oct 22 '25 05:10

Christian Held


I've previously had a similar problem with background tasks, you might need to create a scope.

I've adapted this code and applied it to your use case.

public class AspNetCoreJobFactory : SimpleJobFactory
{
    IServiceProvider _provider;

    public AspNetCoreJobFactory(IServiceProvider provider)
    {
        _provider = provider;
    }

    public override IJob NewJob(TriggerFiredBundle bundle, IScheduler scheduler)
    {
        try
        {
            using(var serviceScope = _provider.CreateScope())
            {
                var services = serviceScope.ServiceProvider.
                return (IJob)services.GetService(bundle.JobDetail.JobType);
            }
        }
        catch(Exception e)
        {
            throw new SchedulerException(string.Format("Problem while instantiating job '{0}' from the AspNet Core IOC.", bundle.JobDetail.Key), e);
        }
    }
}
like image 36
Shoejep Avatar answered Oct 22 '25 05:10

Shoejep



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!