Cannot find IDirectoryScanListener when DirectoryScanJob is executed
Describe the bug
After registering a DirectoryScanJob with an associated trigger, when the job is invoked, an exception is thrown:
Quartz.JobExecutionException: IDirectoryScanListener named 'LogChangesListener' not found in SchedulerContext
at Quartz.Job.DirectoryScanJobModel.GetListener(JobDataMap mergedJobDataMap, SchedulerContext schedCtxt)
at Quartz.Job.DirectoryScanJobModel.GetInstance(IJobExecutionContext context)
at Quartz.Job.DirectoryScanJob.Execute(IJobExecutionContext context)
at Quartz.Simpl.MicrosoftDependencyInjectionJobFactory.ScopedJob.Execute(IJobExecutionContext context)
at Quartz.Core.JobRunShell.Run(CancellationToken cancellationToken)
Version used
.NET 6.0 Quartz 3.4.0 Quartz.Extensions.DependencyInjection 3.4.0 Quartz.Extensions.Hosting 3.4.0 Quartz.Jobs 3.4.0
To Reproduce
I add Quartz to the service application as follows:
services.AddQuartz(q =>
{
q.UseMicrosoftDependencyInjectionJobFactory();
q.AddDirectoryListeners(hostContext.Configuration, services);
})
.AddQuartzHostedService(options =>
{
options.WaitForJobsToComplete = true;
options.AwaitApplicationStarted = true;
});
My AddDirectoryListeners() extension is written as follows, in order to dynamically add DirectoryScanJobs based on appsettings.json configuration:
public static IServiceCollectionQuartzConfigurator AddDirectoryListeners(
this IServiceCollectionQuartzConfigurator quartz,
IConfiguration config,
IServiceCollection services)
{
var listeners = Assembly.GetExecutingAssembly()
.GetTypes()
.Where(type => typeof(IDirectoryScanListener).IsAssignableFrom(type) && !type.IsInterface);
foreach (var listener in listeners)
{
services.AddTransient(listener);
}
var listenerConfigs = config.GetSection("Quartz")
.GetChildren()
.Where(c => c.Key.EndsWith("Listener"))
.ToList();
foreach (var listenerConfig in listenerConfigs)
{
string listenerName = listenerConfig.Key;
string jobSchedule = config[$"Quartz:{listenerName}:Schedule"];
string jobPaths = config[$"Quartz:{listenerName}:Paths"];
var jobKey = new JobKey(listenerName, "DirectoryScanners");
quartz.AddJob<DirectoryScanJob>(opts => opts
.WithIdentity(jobKey)
.UsingJobData(DirectoryScanJob.DirectoryNames, jobPaths)
.UsingJobData(DirectoryScanJob.DirectoryScanListenerName, listenerName));
quartz.AddTrigger(opts => opts
.ForJob(jobKey)
.WithIdentity(listenerName + "-trigger")
.WithCronSchedule(jobSchedule));
}
return quartz;
}
By convention, my IDirectoryScanListener classes end with "Listener" and are configured in appsettings.json with the class name as follows:
{
"Quartz": {
"LogChangesListener": {
"Schedule": "15/30 * * * * ?",
"Paths": "C:\\Temp\\Logs;C:\\Temp\\Other"
}
}
}
Finally, my sample listener class. Note that it uses DI, so I'd hope it would be instantiated at the time of the job executing:
public class LogChangesListener : IDirectoryScanListener
{
private readonly ILogger<LogChangesListener> _log;
public LogChangesListener(ILogger<LogChangesListener> logger)
{
_log = logger;
}
public void FilesDeleted(IReadOnlyCollection<FileInfo> deletedFiles)
{
foreach (FileInfo file in deletedFiles)
_log.LogInformation("Deleted file {fileName}", file.Name);
}
public void FilesUpdatedOrAdded(IReadOnlyCollection<FileInfo> updatedFiles)
{
foreach (FileInfo file in updatedFiles)
_log.LogInformation("Added/Updated file {fileName}", file.Name);
}
}
Expected behavior
I'd expect that when the DirectoryScanJob executes, the appropriate IDirectoryScanListener as registered with DirectoryScanJob.DirectoryScanListenerName gets instantiated and used.
Did you get this working already?
Did you get this working already?
No, it's still not working.