Quartznet: Allow custom configuration via Options pattern

Created on 10 Sep 2020  路  9Comments  路  Source: quartznet/quartznet

Is your feature request related to a problem? Please describe.
"I'm always frustrated when" I cannot access IServiceProvider in the configure action of AddQuartz.

Describe the solution you'd like
AddQuartz should have an overload like AddQuartz(this IServiceCollection serviceCollection, Action<IServiceProvider, IServiceCollectionQuartzConfigurator>? configure = null) similar to eg. AddContext.

Describe alternatives you've considered
Alternatively Quartz could expose an options interface, and I could configure with eg. IConfigureOptions<QuartzOptions>. For example AddCors does this with CorsOptions. One can use IConfigureOptions<CorsOptions> to configure cors and access IServiceProvider.

As a workaround I can manually bootstrap the dependencies I'd like to access in AddQuartz.

Additional context
My use cases are similar to Andrew Lock's described in this blog post. Hope you find some inspiration there.

Many thanks!

EDIT: To be clear, this is a minor improvement on the configuration interface as feedback was requested.

All 9 comments

I think the overload your described AddQuartz(this IServiceCollection serviceCollection, Action<IServiceProvider, IServiceCollectionQuartzConfigurator>? configure = null) makes quite a lot of sense. Would you like offer a PR to add one?

On second note if there's something that is hard to get configured in Quartz via the provided configuration API it might also make sense to add typed helpers there, not sure about your use case though.

I'm afraid I'm not familiar enough with Quartz to create a PR with a reasonable work estimate. So, sorry, not this time.

I accidentally linked wrong issue regarding the feedback, it's fixed now.

All fine, I'll see what I can do 馃憤

To clarify, here's a simple use case with strongly typed settings:

.AddQuartz((serviceProvider, configurator) => {
    var cron = serviceProvider
        .GetRequiredService<IOptions<MyOptions>>()
        .Value
        .Cron;
    configurator.AddTrigger(trigger => trigger
        .ForJob("MyJob")
        .WithCronSchedule(cron));
});

For file-based, auto-refreshed, schedule configuration there's also the XML option, but you have probably already checked that out too. But I see the use case for retrieving things from own configuration/service infrastructure.

My 2-cent: adding such an overload will be very complicated, as the IServiceProvider is not available from ConfigureServices (unless you call services.BuildServiceProvider() to create a temporary DI, but it's a discouraged practice, which should even flag an alert in recent VS/.NET Core versions).

The use case described in @ljani's latest post would be easily solved if Quartz.NET used Microsoft.Extensions.Options (as suggested in the OP and discussed recently in the OpenIddict repo). In this case, doing it inline should be as simple as:

services.AddOptions<QuartzOptions>()
    .Configure<IOptions<MyOptions>>((options, dep) =>
    {
        options.AddTrigger(trigger => trigger
            .ForJob("MyJob")
            .WithCronSchedule(dep.Value.Cron));
    });

Thanks @kevinchalet for chiming in! I did a quick look earlier and this service provider indeed seemed quite hard to achieve. Now I need to study the options model 馃檪

So I've done changes required to achieve @kevinchalet 's proposed idea with PR https://github.com/quartznet/quartznet/pull/978 . Hopefully this is nor more in line with expectations from options pattern and allows to do the Configure.

@lahma Looks great for my needs, thanks!

Was this page helpful?
0 / 5 - 0 ratings