Navigation Menu

Skip to content

Instantly share code, notes, and snippets.

@cleftheris
Last active May 21, 2022 16:37
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save cleftheris/a08b91fe8550c1492272991c456a42a0 to your computer and use it in GitHub Desktop.
Save cleftheris/a08b91fe8550c1492272991c456a42a0 to your computer and use it in GitHub Desktop.
Scoped dependency resolving in SchedulerHostedService
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Scheduling.Cron;
namespace Scheduling
{
public class SchedulerHostedService : HostedService
{
public event EventHandler<UnobservedTaskExceptionEventArgs> UnobservedTaskException;
private readonly List<SchedulerTaskWrapper> _scheduledTasks = new List<SchedulerTaskWrapper>();
private readonly IServiceScopeFactory _serviceScopeFactory;
public SchedulerHostedService(IEnumerable<IScheduledTask> scheduledTasks, IServiceScopeFactory serviceScopeFactory) {
var referenceTime = DateTime.UtcNow;
foreach (var scheduledTask in scheduledTasks) {
_scheduledTasks.Add(new SchedulerTaskWrapper {
Schedule = CrontabSchedule.Parse(scheduledTask.Schedule),
Task = scheduledTask,
NextRunTime = referenceTime
});
}
_serviceScopeFactory = serviceScopeFactory ?? throw new ArgumentNullException(nameof(serviceScopeFactory));
}
protected override async Task ExecuteAsync(CancellationToken cancellationToken) {
while (!cancellationToken.IsCancellationRequested) {
await ExecuteOnceAsync(cancellationToken);
await Task.Delay(TimeSpan.FromMinutes(1), cancellationToken);
}
}
private async Task ExecuteOnceAsync(CancellationToken cancellationToken) {
var taskFactory = new TaskFactory(TaskScheduler.Current);
var referenceTime = DateTime.UtcNow;
var tasksThatShouldRun = _scheduledTasks.Where(t => t.ShouldRun(referenceTime)).ToList();
foreach (var taskThatShouldRun in tasksThatShouldRun) {
taskThatShouldRun.Increment();
await taskFactory.StartNew(
async () => {
try {
using (var scope = _serviceScopeFactory.CreateScope()) {
var t = taskThatShouldRun.Task.GetType();
var method = t.GetMethod("Invoke", BindingFlags.Instance | BindingFlags.Public);
var arguments = method.GetParameters()
.Select(a => a.ParameterType == typeof(CancellationToken) ? cancellationToken : scope.ServiceProvider.GetService(a.ParameterType))
.ToArray();
//invoke.
if (typeof(Task).Equals(method.ReturnType)) {
await (Task)method.Invoke(taskThatShouldRun.Task, arguments);
} else {
method.Invoke(taskThatShouldRun.Task, arguments);
}
}
} catch (Exception ex) {
var args = new UnobservedTaskExceptionEventArgs(
ex as AggregateException ?? new AggregateException(ex));
UnobservedTaskException?.Invoke(this, args);
if (!args.Observed) {
throw;
}
}
},
cancellationToken);
}
}
private class SchedulerTaskWrapper
{
public CrontabSchedule Schedule { get; set; }
public IScheduledTask Task { get; set; }
public DateTime LastRunTime { get; set; }
public DateTime NextRunTime { get; set; }
public void Increment() {
LastRunTime = NextRunTime;
NextRunTime = Schedule.GetNextOccurrence(NextRunTime);
}
public bool ShouldRun(DateTime currentTime) {
return NextRunTime < currentTime && LastRunTime != NextRunTime;
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment