在TopShelf和Quartz范围问题中使用Microsoft DependencyInjection

时间:2018-10-21 11:58:25

标签: dependency-injection windows-services quartz-scheduler topshelf

我使用TopShelf和Quartz将某些作业安排为Windows服务。对于依赖注入,我使用Microsoft.Extensions.DependencyInjection包。我的问题是作用域功能,我想在作用域生命周期中配置服务时(例如ISampleService),每轮运行作业,处置作用域生命周期服务,并在下一轮再次实例化,但是作用域生命周期的行为类似于单例生命周期。以下是我的代码:

依赖项:

using Microsoft.Extensions.DependencyInjection;
using Quartz;
using Quartz.Impl;
using Quartz.Spi;
using System;
using System.Collections.Generic;
using System.Configuration;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Topshelf;

主要方法:

static void Main(string[] args)
{
    var provider = RegisterServices();

    HostFactory.Run(cfg =>
    {
        cfg.Service<SampleTask>(s =>
        {
            s.ConstructUsing(() => provider.GetService<SampleTask>());
            s.WhenStarted(async f => await f.Start(provider));
            s.WhenStopped(f => f.Stop());
        });

        cfg.RunAsLocalSystem();
        cfg.SetDescription("description");
        cfg.SetDisplayName("name");
        cfg.SetServiceName("service");
    });
}

RegisterServices方法

public static ServiceProvider RegisterServices()
{
    IServiceCollection services = new ServiceCollection();

    services.AddSingleton<ISchedulerFactory, StdSchedulerFactory>();

    services.AddTransient<SampleTask>();
    services.AddTransient<IJob, SampleJob>();
    services.AddScoped<ISampleService, SampleService>

    var provider = services.BuildServiceProvider();

    return provider;
}

JobFactory:

public class JobFactory : IJobFactory
{
    protected readonly IServiceProvider Container;

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

    public IJob NewJob(TriggerFiredBundle bundle, IScheduler scheduler)
    {
        return Container.GetService(bundle.JobDetail.JobType) as IJob;
    }

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

任务:

public class SampleTask
{
    private readonly ISchedulerFactory _schedulerFactory;

    public SampleTask(ISchedulerFactory schedulerFactory)
    {
        _schedulerFactory = schedulerFactory;
    }

    public async Task Start(ServiceProvider provider)
    {
        var scheduler = await _schedulerFactory.GetScheduler();
        scheduler.JobFactory = new JobFactory(provider);

        var job = JobBuilder.Create<IJob>().Build();

        var trigger = TriggerBuilder.Create()
            .WithIdentity("Sample1", "SampleGroup")
            .WithSimpleSchedule(x => x
                .WithIntervalInMinutes(10)
                .RepeatForever())
            .StartNow()
            .Build();

        await scheduler.ScheduleJob(job, trigger);

        await scheduler.Start();
    }

    public bool Stop()
    {
        return true;
    }
}

工作:

public class SampleJob : IJob
{
    private readonly ISampleService _sampleService;

    public SampleJob(ISampleService sampleService)
    {
        _sampleService = sampleService;
    }

    public async Task Execute(IJobExecutionContext context)
    {
        await Task.FromResult(0);
    }
}

1 个答案:

答案 0 :(得分:1)

我创建了一个WorkerService项目,并在csproj文件中添加了一些必需的软件包,如下所示:

<Project Sdk="Microsoft.NET.Sdk.Worker">

  <PropertyGroup>
    <TargetFramework>netcoreapp3.1</TargetFramework>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Microsoft.Extensions.Hosting" Version="3.1.9" />
    <PackageReference Include="Topshelf" Version="4.2.1" />
    <PackageReference Include="Topshelf.Extensions.Hosting" Version="0.4.0" />
    <PackageReference Include="Quartz" Version="3.2.2" />
  </ItemGroup>
</Project>
    

要支持瞬态,作用域和单例,我们需要添加一些类

public class JobFactory : IJobFactory
{
    private readonly IServiceProvider _serviceProvider;

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

    public IJob NewJob(TriggerFiredBundle bundle, IScheduler scheduler)
    {
        return _serviceProvider.GetRequiredService<QuartzJobRunner>();
    }

    public void ReturnJob(IJob job)
    {
        // we let the DI container handler this
    }
}

[DisallowConcurrentExecution]
public class QuartzJobRunner : IJob
{
    private readonly IServiceProvider _serviceProvider;

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

    public async Task Execute(IJobExecutionContext context)
    {
        using (var scope = _serviceProvider.CreateScope())
        {
            if (scope.ServiceProvider
    .GetRequiredService(context.JobDetail.JobType) is IJob job)
                await job.Execute(context);
        }
    }
}

public class QuartzHostedService : BackgroundService
{
    private readonly ISchedulerFactory _schedulerFactory;
    private readonly IJobFactory _jobFactory;

    public QuartzHostedService(ISchedulerFactory schedulerFactory,
     IJobFactory jobFactory)
    {
        _schedulerFactory = schedulerFactory;
        _jobFactory = jobFactory;
    }
    
    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        var scheduler = await _schedulerFactory.GetScheduler(stoppingToken);
        scheduler.JobFactory = _jobFactory;

        var jobDetail = JobBuilder
            .Create<Job1>()
            .WithIdentity("Job1")
            .Build();

        var trigger = TriggerBuilder.Create()
            .WithIdentity("Trigger1")
            .WithSimpleSchedule(x => x
                .WithIntervalInSeconds(15)
                .RepeatForever())
            .StartNow()
            .Build();

        await scheduler.ScheduleJob(jobDetail, trigger, stoppingToken);

        await scheduler.Start(stoppingToken);
    }
}

然后我创建要实现我的逻辑的工作

[DisallowConcurrentExecution]
public class Job1 : IJob
{
    private readonly ILogger<Job1> _logger;
    private readonly Service1 _service1;

    public Job1(ILogger<Job1> logger, Service1 service1)
    {
        _logger = logger;
        _service1 = service1;
    }

    public Task Execute(IJobExecutionContext context)
    {
        _logger.LogInformation($"Service Id: {_service1.Id}");

        return Task.CompletedTask;
    }
}

public class Service1
{
    public string Id { get; set; }

    public Service1()
    {
        Id = Guid.NewGuid().ToString();
    }
}

主班

public class Program
{
    public static void Main(string[] args)
    {
        var hostBuilder = CreateHostBuilder(args);

        if (Debugger.IsAttached)
        {
            hostBuilder.Build().Run();
        }
        else
        {
            hostBuilder.RunAsTopshelfService(hc =>
            {
                hc.SetDisplayName("Task1");
                hc.SetDescription("Task1");
                hc.SetServiceName("Task1");
            });
        }
    }

    public static IHostBuilder CreateHostBuilder(string[] args) =>
        Host.CreateDefaultBuilder(args)
            .ConfigureServices((hostContext, services) =>
            {
                services.AddSingleton<IJobFactory, JobFactory>();
                services.AddSingleton<ISchedulerFactory, StdSchedulerFactory>();
                services.AddSingleton<QuartzJobRunner>();
                services.AddScoped<Job1>();

                services.AddSingleton<Service1>();
                //services.AddScoped<Service1>();
                //services.AddTransient<Service1>();

                services.AddHostedService<QuartzHostedService>();
            });
}

运行项目后,您可以检查日志并通过检查Service1 Id确认解决​​方案是否有效。

请注意,在ConfigureServices中,以下依赖项定义不得更改

services.AddSingleton<IJobFactory, JobFactory>();
services.AddSingleton<ISchedulerFactory, StdSchedulerFactory>();
services.AddSingleton<QuartzJobRunner>();
services.AddScoped<Job1>();

通过更改Service1类的依赖范围,您可以看到所有依赖级别都受支持。