Ошибка доступа к службе, которая использует DbContext в моем задании quartz

#asp.net-core #asp.net-web-api #quartz.net

#asp.net-core #asp.net-web-api #quartz.net

Вопрос:

Я создаю веб-API, используя ASP.NET Ядро и теперь у меня возникла проблема с запланированными заданиями quartz. Задания, которые у меня есть, получат доступ к моим службам для обновления базы данных. После некоторых исследований я понял, как выполнить внедрение зависимостей, чтобы мои задания могли получать доступ к службам, вот как я переопределил фабрику заданий:

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

и я добавил эту строку в свою startup configure:

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

Наконец, я добавил эти две строки в свой ConfigureServices метод:

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

прямо сейчас я получаю это исключение при попытке выполнить задание, похоже, это потому, что моя служба использует DbContext , как я могу это решить?

Не удается использовать службу с ограниченной областью действия ‘RHP.data.RHPDbContext’ из одноэлементного ‘RHP.data.IServices.Администрирование.IUserService ‘.

Комментарии:

1. Можете ли вы перейти services.AddSingleton<IUserService, UserService>(); на services.AddTransient<IUserService, UserService>(); ?

2. @Shoejep Я только что попробовал это, то же самое исключение

Ответ №1:

После игры с Quartz (версия 3.2.3) похоже, что вам не нужно писать свой собственный JobFactory , чтобы использовать Microsoft DI. (См . ASP.NET Интеграция с ядром и интеграция с Microsoft DI):

Добавьте Quartz.AspNetCore пакет nuget, и вы сможете использовать службы, подобные этому:

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

Однако, если вы не можете использовать текущую версию Quartz.AspNetCore , вы все равно
можете использовать IServiceProvider as dependency в своем Job классе и разрешать службы там:

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

        // ...
    }
}
 

Таким Job образом, класс контролирует время жизни служб с ограниченной областью действия.

Комментарии:

1. спасибо за ваш ответ, на самом деле я использую версию 3.2.3, которая идеальна. Сейчас я пытаюсь адаптироваться к вашему решению, но у меня есть последняя проблема с доступом к службам на моей работе, когда я ввожу конструктор, который передает в службу, задание, похоже, вообще не вызывается, как мне получить доступ к моей службе, если я не использую новый конструктор (ps: Раньше я решал эту конкретную проблему при переопределении фабрики заданий)

Ответ №2:

Ранее у меня была аналогичная проблема с фоновыми задачами, возможно, вам потребуется создать область видимости.

Я адаптировал этот код и применил его к вашему варианту использования.

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

Комментарии:

1. Я попробовал ваш код, задание больше не выдает исключение, но DbContext, похоже, не работает, все, к чему я пытаюсь получить доступ, пустое или нулевое. есть идеи, почему?

2. @YOUSFIMohamedWalid Хм, странно, я не уверен, я полагаю, вы используете EF Core? Вы можете проверить строку подключения / проверить, что она переводится в SQL или что она работает в базе данных.

3. когда я обращаюсь к тем же методам в той же службе за пределами задания quartz, я получаю свои данные без проблем

4. Приведенный выше код serviceScope немедленно удаляет экземпляр. Удаление области удаляет все разрешенные службы, в частности возвращенный объект и его зависимости. Таким образом, ваш DbContext компьютер не находится в пригодном для использования состоянии.

5. Спасибо @ChristianHeld, это имело бы смысл, хотя, к сожалению, я не знаю, как изменить код, чтобы он размещался правильно и по-прежнему следовал шаблону OP.