Services/JobService.cs
using System;
using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; using Cronos; namespace pscommander { public class JobService { private PowerShellService _powerShellService; private readonly MenuService _menuService; private readonly List<CronJob> _cronJobs; private CancellationTokenSource _cancelSource; public JobService(PowerShellService powerShellService, MenuService menuService) { _powerShellService = powerShellService; _cronJobs = new List<CronJob>(); _menuService = menuService; } public void ScheduleJobs(IEnumerable<Schedule> schedules) { if (_cancelSource != null) { _cancelSource.Cancel(); _cancelSource = null; } _cancelSource = new CancellationTokenSource(); foreach(var job in _cronJobs) { job.Dispose(); } _cronJobs.Clear(); foreach(var schedule in schedules) { ScheduleJob(schedule); } } private void ScheduleJob(Schedule schedule) { var job = new CronJob(schedule, _powerShellService, _menuService); job.StartAsync(_cancelSource.Token).Wait(); _cronJobs.Add(job); } } public class CronJob : IDisposable { private System.Timers.Timer _timer; private readonly CronExpression _expression; private readonly Schedule _schedule; private readonly PowerShellService _powerShellService; private readonly MenuService _menuService; public CronJob(Schedule schedule, PowerShellService powerShellService, MenuService menuService) { _schedule = schedule; _expression = CronExpression.Parse(schedule.Cron); _powerShellService = powerShellService; _menuService = menuService; } public virtual async Task StartAsync(CancellationToken cancellationToken) { await ScheduleJob(cancellationToken); } protected virtual async Task ScheduleJob(CancellationToken cancellationToken) { var next = _expression.GetNextOccurrence(DateTimeOffset.Now, TimeZoneInfo.Local); if (next.HasValue) { var delay = next.Value - DateTimeOffset.Now; if (delay.TotalMilliseconds <= 0) // prevent non-positive values from being passed into Timer { await ScheduleJob(cancellationToken); } _timer = new System.Timers.Timer(delay.TotalMilliseconds); _timer.Elapsed += async (sender, args) => { _timer.Dispose(); // reset and dispose timer _timer = null; if (!cancellationToken.IsCancellationRequested) { await DoWork(cancellationToken); } if (!cancellationToken.IsCancellationRequested) { await ScheduleJob(cancellationToken); // reschedule next } }; _timer.Start(); } await Task.CompletedTask; } public virtual async Task DoWork(CancellationToken cancellationToken) { await Task.CompletedTask; try { _powerShellService.Execute(_schedule.Action); } catch (Exception ex) { _menuService.ShowError(ex.Message); } } public virtual async Task StopAsync(CancellationToken cancellationToken) { _timer?.Stop(); await Task.CompletedTask; } public virtual void Dispose() { _timer?.Dispose(); } } } |