ASP Core 2.1 对 Quartz 的依赖注入

ASP Core 2.1 Dependency Injection to Quartz

我正在尝试将服务注入我的 SendEmailJob class。 我使用标准 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;
        }
    }
}

JobFactory 的实现:

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 或在任何其他作用域服务上失败。

你能解释一下如何正确注入吗?

问题是您将 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();
    }
} 

我不确定您是否能够解决您在评论中提到的已处置 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) {

        }
    }
}