openiddict-core: Quartz exception after upgrade

After upgrading to latest OpenIddict (3.0.3) and Quartz (3.3.1), seeing below runtime exception on startup.

This doesn’t occur with 3.0.2 and Quartz 3.2.4. Rolling back is viable workaround. Happy to provide further details.

Configuration:

// Add Quartz.NET for recurring datastore pruning
services.AddQuartz(options =>
{
    options.UseMicrosoftDependencyInjectionJobFactory();
    options.UseSimpleTypeLoader();
    options.UseInMemoryStore();
});

// Register the Quartz.NET service and configure it to block shutdown until jobs are complete
services.AddQuartzHostedService(options => options.WaitForJobsToComplete = true);

...

services.AddOpenIddict()
    .AddCore(options =>
    {
        // Enable Quartz.NET integration
        options.UseQuartz(options =>
        {
            options.SetMinimumTokenLifespan(environment.IsDevelopment() ? TimeSpan.FromHours(1) : TimeSpan.FromHours(24));
        });
    })
...

Exception:

An error occurred instantiating job to be executed. job= 'OpenIddict.Quartz.OpenIddictQuartzJob, message=Multiple constructors accepting all given argument types have been found in type 'OpenIddict.Quartz.OpenIddictQuartzJob'. There should only be one applicable constructor.'
Quartz.SchedulerException: Problem instantiating type 'OpenIddict.Quartz.OpenIddictQuartzJob: Multiple constructors accepting all given argument types have been found in type 'OpenIddict.Quartz.OpenIddictQuartzJob'. There should only be one applicable constructor.'
 ---> System.InvalidOperationException: Multiple constructors accepting all given argument types have been found in type 'OpenIddict.Quartz.OpenIddictQuartzJob'. There should only be one applicable constructor.
   at Microsoft.Extensions.DependencyInjection.ActivatorUtilities.TryFindMatchingConstructor(Type instanceType, Type[] argumentTypes, ConstructorInfo& matchingConstructor, Nullable`1[]& parameterMap)
   at Microsoft.Extensions.DependencyInjection.ActivatorUtilities.FindApplicableConstructor(Type instanceType, Type[] argumentTypes, ConstructorInfo& matchingConstructor, Nullable`1[]& matchingParameterMap)
   at Microsoft.Extensions.DependencyInjection.ActivatorUtilities.CreateFactory(Type instanceType, Type[] argumentTypes)
   at System.Collections.Concurrent.ConcurrentDictionary`2.GetOrAdd(TKey key, Func`2 valueFactory)
   at Quartz.Simpl.JobActivatorCache.CreateInstance(IServiceProvider serviceProvider, Type jobType)
   at Quartz.Simpl.PropertySettingJobFactory.NewJob(TriggerFiredBundle bundle, IScheduler scheduler)
   at Quartz.Core.JobRunShell.Initialize(QuartzScheduler sched, CancellationToken cancellationToken)
   --- End of inner exception stack trace --- [See nested exception: System.InvalidOperationException: Multiple constructors accepting all given argument types have been found in type 'OpenIddict.Quartz.OpenIddictQuartzJob'. There should only be one applicable constructor.
   at Microsoft.Extensions.DependencyInjection.ActivatorUtilities.TryFindMatchingConstructor(Type instanceType, Type[] argumentTypes, ConstructorInfo& matchingConstructor, Nullable`1[]& parameterMap)
   at Microsoft.Extensions.DependencyInjection.ActivatorUtilities.FindApplicableConstructor(Type instanceType, Type[] argumentTypes, ConstructorInfo& matchingConstructor, Nullable`1[]& matchingParameterMap)
   at Microsoft.Extensions.DependencyInjection.ActivatorUtilities.CreateFactory(Type instanceType, Type[] argumentTypes)
   at System.Collections.Concurrent.ConcurrentDictionary`2.GetOrAdd(TKey key, Func`2 valueFactory)
   at Quartz.Simpl.JobActivatorCache.CreateInstance(IServiceProvider serviceProvider, Type jobType)
   at Quartz.Simpl.PropertySettingJobFactory.NewJob(TriggerFiredBundle bundle, IScheduler scheduler)
   at Quartz.Core.JobRunShell.Initialize(QuartzScheduler sched, CancellationToken cancellationToken)]
[10:58:38 INF] All triggers of Job OpenIddict.Quartz.OpenIddictQuartzJob set to ERROR state.

Further technical details

  • ASP.NET Core version: 5.0.5
  • Visual Studio 16.9

About this issue

  • Original URL
  • State: closed
  • Created 3 years ago
  • Comments: 22 (11 by maintainers)

Most upvoted comments

@NoahStahl Quartz.NET 3.3.2 is now available on NuGet thanks to great input from both @kevinchalet and @ArturDorochowicz . Please try it out and hopefully your problems are gone now.

@lahma I just gave it a try and it works like a charm! Thanks for fixing it so fast, much appreciated!

I’ll open a separate thread to track future improvements (like removing the manual scope creation, since Quartz.NET now always creates a scope for us)

@lahma Thanks Marko! Great response time. I actually refactored my particular project in the meantime to a different method of scheduling the task, so I won’t be able to confirm fix. But feel free to close the issue regardless.

I’ll try to create a hybrid approach this evening and if it flies, a new release shortly after. I’ll probably will be pinging you two for a review.

@NoahStahl this was discussed at length here: https://github.com/openiddict/openiddict-core/issues/361. TL;DR Quartz.NET was by far the best option and its extensibility allows scenarios that couldn’t be implemented in a trivial IHostedService (e.g web farm scenarios where you want to synchronize jobs execution).

@kevinchalet Given the evident possibility of Quartz integration as breaking change source, and the increasing prevalence of supply-chain attacks, any reason Quartz was used for the pruning job?

Well, if we didn’t trust any third-party author, we wouldn’t depend on anything and we’d have to continuously re-invent the wheel. It’s been the argument used by MSFT for years and it’s definitely not an approach I want to adopt.

@lahma would it be safer to revert the DI change before folks start depending on the new behavior and give you a bit more time to decide what’s the best approach? (maybe something hybrid, that uses DI first and falls back to ActivatorUtilities if the job was not registered as a service?)