c# - Quartz.net Core 中的依赖注入(inject)未找到注册服务

标签 c# dependency-injection .net-core quartz.net

我在尝试设置 Quartz 时遇到 DI 注册问题,该作业是一个简单的测试作业,用于确认 DI 正在工作(仅将文本输出到控制台)。

抛出错误的代码位于最后一个类JobFactory中。

Program.cs

static async Task Main(string[] args)
{

    var isService = !(Debugger.IsAttached || ((IList)args).Contains("--console"));

    var path = Path.GetDirectoryName(new Uri(Assembly.GetExecutingAssembly().CodeBase).LocalPath);

    var webHost = new HostBuilder()
        .ConfigureAppConfiguration((cxt, config) =>
        {
            config.SetBasePath(path);
            config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true);
            config.AddJsonFile($"appsettings.{environmentName}.json", optional: true, reloadOnChange: true);
            config.AddEnvironmentVariables();

            if (args != null)
            {
                config.AddCommandLine(args);
            }

            Log.Logger = new LoggerConfiguration()
                    .ReadFrom.Configuration(config.Build())
                    .Enrich.FromLogContext()
                    .CreateLogger();
        })
        .ConfigureServices((cxt, services) =>
        {
            var configuration = cxt.Configuration;

            var bw = new BackgroundWorker(services.BuildServiceProvider());
            services.AddSingleton<IHostedService>(bw);
            services.AddLogging(loggingBuilder => loggingBuilder.AddSerilog(dispose: true));

            //services.AddSingleton<ISchedulerFactory, SchedulerFactory>();
            //services.AddSingleton<ScopedJobFactory>();

            services.AddScoped(_ => new SomeJob(configuration));
            //services.AddTransient<IJob>(_ => new SomeJob(configuration));

    var token = tokenSource.Token;
    if (isService)
    {
        await webHost.RunAsServiceAsync(token);
    }
    else
    {
        await webHost.RunConsoleAsync(token);
    }
}

设置 Quartz 作业工厂:

private static async Task<IScheduler> InitiateQuartzScheduler(IServiceProvider serviceProvider)
{
    try
    {
        var factory = new StdSchedulerFactory();
        var scheduler = await factory.GetScheduler();
        scheduler.JobFactory = new JobFactory(serviceProvider);

        await scheduler.Start();

        return scheduler;

    }
    catch (SchedulerException se)
    {
        Log.Logger.Fatal(se, "Error at starting the Quartz Scheduler");
    }

    return null;
}

后台 worker :

private class BackgroundWorker : IHostedService
{
    private IScheduler quartzScheduler;
    private readonly IServiceProvider serviceProvider;

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

    public async Task StartAsync(CancellationToken cancellationToken)
    {
        //Log.Logger = SetupSerilog();
        Log.Logger.Information("Starting Quartz BackgroundWorker.");

        quartzScheduler = await InitiateQuartzScheduler(serviceProvider);
    }

    public async Task StopAsync(CancellationToken cancellationToken)
    {
        Log.Logger.Information("Quartz Background Worker is stopping.");
    }
}

作业工厂(发生错误的位置):

internal class JobFactory : IJobFactory
{
    protected readonly IServiceProvider serviceProvider;

    protected readonly ConcurrentDictionary<IJob, IServiceScope> _scopes = new ConcurrentDictionary<IJob, IServiceScope>();

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

    public IJob NewJob(TriggerFiredBundle bundle, IScheduler scheduler)
    {
        var scope = serviceProvider.CreateScope();
        IJob job;

        try
        {
            //
            // **ERROR HERE**
            //
            job = scope.ServiceProvider.GetRequiredService(bundle.JobDetail.JobType) as IJob;

        }
        catch
        {
            // Failed to create the job -> ensure scope gets disposed
            scope.Dispose();
            throw;
        }

        // Add scope to dictionary so we can dispose it once the job finishes
        if (!_scopes.TryAdd(job, scope))
        {
            // Failed to track DI scope -> ensure scope gets disposed
            scope.Dispose();
            throw new Exception("Failed to track DI scope");
        }

        return job;
    }

    public void ReturnJob(IJob job)
    {
        if (_scopes.TryRemove(job, out var scope))
        {
            // The Dispose() method ends the scope lifetime.
            // Once Dispose is called, any scoped services that have been resolved from ServiceProvider will be disposed.
            scope.Dispose();
        }
    }
}

运行时错误:

System.InvalidOperationException: 'No service for type 'xxx.yyy.SomeJob' has been registered.'

最佳答案

在添加所有必需的依赖项之前,将为后台工作人员提供一个提供程序。

//...

var configuration = cxt.Configuration;

var bw = new BackgroundWorker(services.BuildServiceProvider()); //<---This service provider
services.AddSingleton<IHostedService>(bw);
services.AddLogging(loggingBuilder => loggingBuilder.AddSerilog(dispose: true));

services.AddScoped(_ => new SomeJob(configuration)); //<--knows nothing about this service

//...or any other service added after services.BuildServiceProvider()

//...

构建服务集合后,集合中的任何更改(添加/删除)都不会影响已构建的提供程序。

在注册工作人员时考虑改变方法并使用延迟委托(delegate)工厂

//...

services.AddSingleton<IHostedService>(serviceProvider => new BackgroundWorker(serviceProvider));

//...

关于c# - Quartz.net Core 中的依赖注入(inject)未找到注册服务,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56634078/

相关文章:

dependency-injection - 使用 Jersey 测试框架的 JUnit 测试中的 CDI

asp.net - 授权与 Active Directory .NET Core 1.1、.Net Framework 的属性交互

.net - DotNet Core HttpClient 不尊重 Mac 上的代理设置

c# - Store 没有实现 IUserRoleStore<TUser> ASP.NET Core Identity

c# - 在使用naudio播放期间调整字节流

c# - Sharepoint ClientContext 在服务中的使用

c# - 何时使用单例、 transient 和使用 Ninject 和 MongoDB 的请求

c# - ASP.NET Core 2 中的依赖注入(inject)抛出异常

c# - 循环依赖树,合理与否

c# - 为什么VS要求不使用内联代码?