ASP Core 2.1 Внедрение зависимостей в кварц - PullRequest
0 голосов
/ 19 сентября 2018

Я пытаюсь внедрить сервисы в мой класс SendEmailJob.Для планирования я использую стандартное внедрение ядра ASP и библиотеку Quartz.Я пытаюсь построить решение на основе этого ответа .Но все же я сталкиваюсь с проблемами с инъекцией.

У меня есть такая настройка кода:

//Startup.cs, ConfigureServices
ServiceAutoConfig.Configure(allServices);
services.AddScoped<IUnitOfWork, UnitOfWork>();

services.AddTransient<IJobFactory, JobFactory>((provider) => new JobFactory(services.BuildServiceProvider()));
services.AddTransient<SendEmailJob>();

//Startup.cs, Configure
app.UseQuartz((quartz) => quartz.AddJob<SendEmailJob>("SendEmailJob", "Email", mailSettings.EmailSchedulerInterval));

Реализация SendEmailJob:

public class SendEmailJob : IJob
{
    private readonly IMessageService _messageService;
    private static bool IsBusy = false;

    public SendEmailJob(IMessageService messageService)
    {
        _messageService = messageService;
    }

    public async Task Execute(IJobExecutionContext context)
    {
        try
        {
            if (IsBusy)
                return;
            IsBusy = true;
            //...
        }
        catch (Exception error)
        {
        }
        finally
        {
            IsBusy = false;
        }
    }
}

Реализация JobFacctory:

public class JobFactory : IJobFactory
{
    protected readonly IServiceProvider _container;

    public JobFactory(IServiceProvider container)
    {
        _container = container;
    }

    public IJob NewJob(TriggerFiredBundle bundle, IScheduler scheduler)
    {
        try
        {
            var res = _container.GetService(bundle.JobDetail.JobType) as IJob;
            return res;
        }
        catch (Exception ex)
        {
            //ERROR-  Cannot resolve 'Quartz.Jobs.SendEmailJob' from root provider because it 
            //        requires scoped service 'BLL.Base.UnitOfWork.Interfaces.IUnitOfWork'.
            throw;
        }
    }

    public void ReturnJob(IJob job)
    {
        (job as IDisposable)?.Dispose();
    }
} 

Реализация Quartz.cs

public class Quartz
{
    private IScheduler _scheduler;
    public static IScheduler Scheduler { get { return Instance._scheduler; } }
    private static Quartz _instance = null;

    public static Quartz Instance
    {
        get
        {
            if (_instance == null)
            {
                _instance = new Quartz();
            }
            return _instance;
        }
    }

    private Quartz()
    {
        Init();
    }

    private async void Init()
    {
        _scheduler = await new StdSchedulerFactory().GetScheduler();
    }

    public IScheduler UseJobFactory(IJobFactory jobFactory)
    {
        Scheduler.JobFactory = jobFactory;
        return Scheduler;
    }

    public async void AddJob<T>(string name, string group, int interval)
        where T : IJob
    {
        IJobDetail job = JobBuilder.Create<T>()
            .WithIdentity(name, group)
            .Build();

        ITrigger jobTrigger = TriggerBuilder.Create()
            .WithIdentity(name + "Trigger", group)
            .StartNow()
            .WithSimpleSchedule(t => t.WithIntervalInSeconds(interval).RepeatForever()) // Mit wiederholung alle interval sekunden
            .Build();

        await Scheduler.ScheduleJob(job, jobTrigger);
    }

    public static async void Start()
    {
        await Scheduler.Start();
    }
}

И реализация UseQuartzExtension:

public static void UseQuartz(this IApplicationBuilder app, Action<Quartz> configuration)
{
    var jobFactory = new JobFactory(app.ApplicationServices);
    Quartz.Instance.UseJobFactory(jobFactory);

    configuration.Invoke(Quartz.Instance);
    Quartz.Start();
}

И при введении IMessageService в SendMailJob возникает ошибка.Потому что это требует UnitOfWork или не работает в любой другой области обслуживания.

Не могли бы вы объяснить, как правильно вводить?

Ответы [ 2 ]

0 голосов
/ 10 декабря 2018

Я не уверен, что вам удалось решить проблему удаленного DbContext, о которой вы упоминали в своем комментарии, но я работаю над выпуском основного приложения .NET с той же проблемой и придумала решение, аналогичное Alex Riabov , но использует параллельный словарь для определения области действия после завершения задания.В результате новый dbcontext внедряется в мою работу при создании новой работы.

 public class QuartzJobFactory : IJobFactory
{
    protected readonly IServiceProvider serviceProvider;
    private ConcurrentDictionary<IJob, IServiceScope> scopes = new ConcurrentDictionary<IJob, IServiceScope>();

    public QuartzJobFactory(IServiceProvider serviceProvider)
    {
        this.serviceProvider = serviceProvider;
    }

    // instantiation of new job
    public IJob NewJob(TriggerFiredBundle bundle, IScheduler scheduler)
    {
        try {
            var scope = serviceProvider.CreateScope();
            var job = scope.ServiceProvider.GetRequiredService(bundle.JobDetail.JobType) as IJob;

            scopes.TryAdd(job, scope);

            return job;
        }
        catch (Exception ex) {
            throw;
        }
    }

    // executes when job is complete
    public void ReturnJob(IJob job)
    {
        try {
            (job as IDisposable)?.Dispose();

            if (scopes.TryRemove(job, out IServiceScope scope))
                scope.Dispose();
        }
        catch (Exception ex) {

        }
    }
}
0 голосов
/ 19 сентября 2018

Проблема в том, что вы регистрируете IUnitOfWork как область действия, но у вас нет какой-либо области на момент ее разрешения.Создайте его, прежде чем разрешить свою работу:

public class JobFactory : IJobFactory, IDisposable
{
    protected readonly IServiceScope _scope;

    public JobFactory(IServiceProvider container)
    {
        _scope = container.CreateScope();
    }

    public IJob NewJob(TriggerFiredBundle bundle, IScheduler scheduler)
    {
        var res = _scope.ServiceProvider.GetService(bundle.JobDetail.JobType) as IJob;
        return res;
    }

    public void ReturnJob(IJob job)
    {
        (job as IDisposable)?.Dispose();
    }

    public void Dispose()
    {
        _scope.Dispose();
    }
} 
...