Skip to content

Commit

Permalink
Changes based on feedback
Browse files Browse the repository at this point in the history
  • Loading branch information
ejsmith committed Jul 24, 2024
1 parent 7397710 commit 303146b
Show file tree
Hide file tree
Showing 13 changed files with 338 additions and 161 deletions.
8 changes: 4 additions & 4 deletions samples/Foundatio.HostingSample/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -89,10 +89,10 @@ public static IHostBuilder CreateHostBuilder(string[] args)
s.AddHealthChecks().AddCheckForStartupActions("Critical");
if (everyMinute)
s.AddCronJob<EveryMinuteJob>("* * * * *");
s.AddDistributedCronJob<EveryMinuteJob>("* * * * *");
if (evenMinutes)
s.AddCronJob("*/2 * * * *", async sp =>
s.AddCronJob("EvenMinutes", "*/2 * * * *", async sp =>
{
var logger = sp.GetRequiredService<ILogger<Program>>();
if (logger.IsEnabled(LogLevel.Information))
Expand All @@ -102,12 +102,12 @@ public static IHostBuilder CreateHostBuilder(string[] args)
});
if (sample1)
s.AddJob(sp => new Sample1Job(sp.GetRequiredService<ILoggerFactory>()), o => o.ApplyDefaults<Sample1Job>().WaitForStartupActions(true).InitialDelay(TimeSpan.FromSeconds(4)));
s.AddJob("Sample1", sp => new Sample1Job(sp.GetRequiredService<ILoggerFactory>()), o => o.ApplyDefaults<Sample1Job>().WaitForStartupActions(true).InitialDelay(TimeSpan.FromSeconds(4)));
if (sample2)
{
s.AddHealthChecks().AddCheck<Sample2Job>("Sample2Job");
s.AddJob<Sample2Job>(true);
s.AddJob<Sample2Job>(o => o.WaitForStartupActions(true));
}
// if you don't specify priority, actions will automatically be assigned an incrementing priority starting at 0
Expand Down
1 change: 0 additions & 1 deletion src/Foundatio.Extensions.Hosting/Jobs/HostedJobOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,4 @@ namespace Foundatio.Extensions.Hosting.Jobs;
public class HostedJobOptions : JobOptions
{
public bool WaitForStartupActions { get; set; }
public string CronSchedule { get; set; }
}
3 changes: 3 additions & 0 deletions src/Foundatio.Extensions.Hosting/Jobs/HostedJobService.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;
using Foundatio.Extensions.Hosting.Startup;
Expand Down Expand Up @@ -51,6 +52,8 @@ private async Task ExecuteAsync(CancellationToken stoppingToken)

try
{
using var activity = FoundatioDiagnostics.ActivitySource.StartActivity("Job " + _jobOptions.Name, ActivityKind.Server);

await runner.RunAsync(stoppingToken).AnyContext();
#if NET8_0_OR_GREATER
await _stoppingCts.CancelAsync().AnyContext();
Expand Down
184 changes: 115 additions & 69 deletions src/Foundatio.Extensions.Hosting/Jobs/JobHostExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,136 +16,182 @@ public static IServiceCollection AddJob(this IServiceCollection services, Hosted
if (jobOptions.JobFactory == null)
throw new ArgumentNullException(nameof(jobOptions), "jobOptions.JobFactory is required");

if (String.IsNullOrEmpty(jobOptions.CronSchedule))
{
return services.AddTransient<IHostedService>(s => new HostedJobService(s, jobOptions, s.GetService<ILoggerFactory>()));
}

if (!services.Any(s => s.ServiceType == typeof(IHostedService) && s.ImplementationType == typeof(ScheduledJobService)))
services.AddTransient<IHostedService, ScheduledJobService>();

if (!services.Any(s => s.ServiceType == typeof(IScheduledJobManager) && s.ImplementationType == typeof(ScheduledJobManager)))
services.AddSingleton<ScheduledJobManager>();

return services.AddTransient(s => new ScheduledJobRegistration(jobOptions.CronSchedule, jobOptions.Name ?? Guid.NewGuid().ToString(), jobOptions.JobFactory));
return services.AddTransient<IHostedService>(s => new HostedJobService(s, jobOptions, s.GetService<ILoggerFactory>()));
}

public static IServiceCollection AddJob(this IServiceCollection services, Func<IServiceProvider, IJob> jobFactory, HostedJobOptions jobOptions)
public static IServiceCollection AddJob<T>(this IServiceCollection services, HostedJobOptions jobOptions = null) where T : class, IJob
{
if (String.IsNullOrEmpty(jobOptions.CronSchedule))
services.AddTransient<T>();
return services.AddTransient<IHostedService>(s =>
{
return services.AddTransient<IHostedService>(s =>
if (jobOptions == null)
{
jobOptions.JobFactory = jobFactory;
jobOptions = new HostedJobOptions();
jobOptions.ApplyDefaults<T>();
}
return new HostedJobService(s, jobOptions, s.GetService<ILoggerFactory>());
});
}

if (!services.Any(s => s.ServiceType == typeof(IHostedService) && s.ImplementationType == typeof(ScheduledJobService)))
services.AddTransient<IHostedService, ScheduledJobService>();
jobOptions.Name ??= typeof(T).FullName;
jobOptions.JobFactory ??= sp => sp.GetRequiredService<T>();
if (!services.Any(s => s.ServiceType == typeof(IScheduledJobManager) && s.ImplementationType == typeof(ScheduledJobManager)))
services.AddSingleton<ScheduledJobManager>();
return new HostedJobService(s, jobOptions, s.GetService<ILoggerFactory>());
});
}

return services.AddTransient(s => new ScheduledJobRegistration(jobOptions.CronSchedule, jobOptions.Name ?? Guid.NewGuid().ToString(), _ => jobFactory(s)));
public static IServiceCollection AddJob<T>(this IServiceCollection services, Action<HostedJobOptionsBuilder> configureJobOptions) where T : class, IJob
{
var jobOptionsBuilder = new HostedJobOptionsBuilder();
jobOptionsBuilder.ApplyDefaults<T>();
jobOptionsBuilder.Name(typeof(T).FullName);
configureJobOptions?.Invoke(jobOptionsBuilder);
return services.AddJob<T>(jobOptionsBuilder.Target);
}

public static IServiceCollection AddJob<T>(this IServiceCollection services, HostedJobOptions jobOptions) where T : class, IJob
public static IServiceCollection AddJob(this IServiceCollection services, Action<HostedJobOptionsBuilder> configureJobOptions)
{
services.AddTransient<T>();
if (String.IsNullOrEmpty(jobOptions.CronSchedule))
{
return services.AddTransient<IHostedService>(s =>
{
if (jobOptions.JobFactory == null)
jobOptions.JobFactory = _ => s.GetRequiredService<T>();
var jobOptionsBuilder = new HostedJobOptionsBuilder();
configureJobOptions?.Invoke(jobOptionsBuilder);
return services.AddJob(jobOptionsBuilder.Target);
}

return new HostedJobService(s, jobOptions, s.GetService<ILoggerFactory>());
});
}
public static IServiceCollection AddJob(this IServiceCollection services, string name, Func<IServiceProvider, IJob> jobFactory, Action<HostedJobOptionsBuilder> configureJobOptions)
{
var jobOptionsBuilder = new HostedJobOptionsBuilder();
jobOptionsBuilder.Name(name).JobFactory(jobFactory);
configureJobOptions?.Invoke(jobOptionsBuilder);
return services.AddJob(jobOptionsBuilder.Target);
}

if (!services.Any(s => s.ServiceType == typeof(IHostedService) && s.ImplementationType == typeof(ScheduledJobService)))
services.AddTransient<IHostedService, ScheduledJobService>();
public static IServiceCollection AddCronJob(this IServiceCollection services, ScheduledJobOptions jobOptions)
{
if (jobOptions.JobFactory == null)
throw new ArgumentNullException(nameof(jobOptions), "jobOptions.JobFactory is required");

if (!services.Any(s => s.ServiceType == typeof(IScheduledJobManager) && s.ImplementationType == typeof(ScheduledJobManager)))
services.AddSingleton<ScheduledJobManager>();
services.AddJobScheduler();

return services.AddTransient(s => new ScheduledJobRegistration(jobOptions.CronSchedule, typeof(T).FullName, jobOptions.JobFactory ?? (_ => s.GetRequiredService<T>())));
return services.AddTransient(s => new ScheduledJobRegistration(jobOptions));
}

public static IServiceCollection AddJob<T>(this IServiceCollection services, bool waitForStartupActions = false) where T : class, IJob
public static IServiceCollection AddCronJob(this IServiceCollection services, Action<ScheduledJobOptionsBuilder> configureJobOptions)
{
return services.AddJob<T>(o => o.ApplyDefaults<T>().WaitForStartupActions(waitForStartupActions));
var jobOptionsBuilder = new ScheduledJobOptionsBuilder();
configureJobOptions?.Invoke(jobOptionsBuilder);
return services.AddCronJob(jobOptionsBuilder.Target);
}

public static IServiceCollection AddCronJob<T>(this IServiceCollection services, string cronSchedule) where T : class, IJob
public static IServiceCollection AddCronJob<T>(this IServiceCollection services, string name, string cronSchedule, Action<ScheduledJobOptionsBuilder> configureJobOptions = null) where T : class, IJob
{
return services.AddJob<T>(o => o.CronSchedule(cronSchedule));
services.AddTransient<T>();
var jobOptionsBuilder = new ScheduledJobOptionsBuilder();
jobOptionsBuilder.Name(name).CronSchedule(cronSchedule).JobFactory(sp => sp.GetRequiredService<T>());
configureJobOptions?.Invoke(jobOptionsBuilder);
return services.AddCronJob(jobOptionsBuilder.Target);
}

public static IServiceCollection AddCronJob(this IServiceCollection services, string cronSchedule, Func<IServiceProvider, CancellationToken, Task> action)
public static IServiceCollection AddCronJob(this IServiceCollection services, string name, string cronSchedule, Func<IServiceProvider, CancellationToken, Task> action)
{
return services.AddJob(o => o.CronSchedule(cronSchedule).JobFactory(sp => new DynamicJob(sp, action)));
return services.AddCronJob(o => o.Name(name).CronSchedule(cronSchedule).JobFactory(sp => new DynamicJob(sp, action)));
}

public static IServiceCollection AddCronJob(this IServiceCollection services, string cronSchedule, Func<IServiceProvider, Task> action)
public static IServiceCollection AddCronJob(this IServiceCollection services, string name, string cronSchedule, Func<IServiceProvider, Task> action)
{
return services.AddJob(o => o.CronSchedule(cronSchedule).JobFactory(sp => new DynamicJob(sp, (xp, _) => action(xp))));
return services.AddCronJob(o => o.Name(name).CronSchedule(cronSchedule).JobFactory(sp => new DynamicJob(sp, (xp, _) => action(xp))));
}

public static IServiceCollection AddCronJob(this IServiceCollection services, string cronSchedule, Func<Task> action)
public static IServiceCollection AddCronJob(this IServiceCollection services, string name, string cronSchedule, Func<Task> action)
{
return services.AddJob(o => o.CronSchedule(cronSchedule).JobFactory(sp => new DynamicJob(sp, (_, _) => action())));
return services.AddCronJob(o => o.Name(name).CronSchedule(cronSchedule).JobFactory(sp => new DynamicJob(sp, (_, _) => action())));
}

public static IServiceCollection AddCronJob(this IServiceCollection services, string cronSchedule, Action<IServiceProvider, CancellationToken> action)
public static IServiceCollection AddCronJob(this IServiceCollection services, string name, string cronSchedule, Action<IServiceProvider, CancellationToken> action)
{
return services.AddJob(o => o.CronSchedule(cronSchedule).JobFactory(sp => new DynamicJob(sp, (xp, ct) =>
return services.AddCronJob(o => o.Name(name).CronSchedule(cronSchedule).JobFactory(sp => new DynamicJob(sp, (xp, ct) =>
{
action(xp, ct);
return Task.CompletedTask;
})));
}

public static IServiceCollection AddCronJob(this IServiceCollection services, string cronSchedule, Action<CancellationToken> action)
public static IServiceCollection AddCronJob(this IServiceCollection services, string name, string cronSchedule, Action<CancellationToken> action)
{
return services.AddJob(o => o.CronSchedule(cronSchedule).JobFactory(sp => new DynamicJob(sp, (_, ct) =>
return services.AddCronJob(o => o.Name(name).CronSchedule(cronSchedule).JobFactory(sp => new DynamicJob(sp, (_, ct) =>
{
action(ct);
return Task.CompletedTask;
})));
}

public static IServiceCollection AddCronJob(this IServiceCollection services, string cronSchedule, Action action)
public static IServiceCollection AddCronJob(this IServiceCollection services, string name, string cronSchedule, Action action)
{
return services.AddJob(o => o.CronSchedule(cronSchedule).JobFactory(sp => new DynamicJob(sp, (_, _) =>
return services.AddCronJob(o => o.Name(name).CronSchedule(cronSchedule).JobFactory(sp => new DynamicJob(sp, (_, _) =>
{
action();
return Task.CompletedTask;
})));
}

public static IServiceCollection AddJob<T>(this IServiceCollection services, Action<HostedJobOptionsBuilder> configureJobOptions) where T : class, IJob
public static IServiceCollection AddDistributedCronJob<T>(this IServiceCollection services, string cronSchedule, Action<ScheduledJobOptionsBuilder> configureJobOptions = null) where T : class, IJob
{
var jobOptionsBuilder = new HostedJobOptionsBuilder();
jobOptionsBuilder.Name(typeof(T).FullName);
services.AddTransient<T>();
var jobOptionsBuilder = new ScheduledJobOptionsBuilder();
jobOptionsBuilder.Name(typeof(T).FullName).Distributed(true).CronSchedule(cronSchedule).JobFactory(sp => sp.GetRequiredService<T>());
configureJobOptions?.Invoke(jobOptionsBuilder);
return services.AddJob<T>(jobOptionsBuilder.Target);
return services.AddCronJob(jobOptionsBuilder.Target);
}

public static IServiceCollection AddJob(this IServiceCollection services, Action<HostedJobOptionsBuilder> configureJobOptions)
public static IServiceCollection AddDistributedCronJob(this IServiceCollection services, string name, string cronSchedule, Func<IServiceProvider, CancellationToken, Task> action)
{
var jobOptionsBuilder = new HostedJobOptionsBuilder();
configureJobOptions?.Invoke(jobOptionsBuilder);
return services.AddJob(jobOptionsBuilder.Target);
return services.AddCronJob(o => o.Name(name).Distributed(true).CronSchedule(cronSchedule).JobFactory(sp => new DynamicJob(sp, action)));
}

public static IServiceCollection AddJob(this IServiceCollection services, Func<IServiceProvider, IJob> jobFactory, Action<HostedJobOptionsBuilder> configureJobOptions)
public static IServiceCollection AddDistributedCronJob(this IServiceCollection services, string name, string cronSchedule, Func<IServiceProvider, Task> action)
{
var jobOptionsBuilder = new HostedJobOptionsBuilder();
configureJobOptions?.Invoke(jobOptionsBuilder);
return services.AddJob(jobFactory, jobOptionsBuilder.Target);
return services.AddCronJob(o => o.Name(name).Distributed(true).CronSchedule(cronSchedule).JobFactory(sp => new DynamicJob(sp, (xp, _) => action(xp))));
}

public static IServiceCollection AddDistributedCronJob(this IServiceCollection services, string name, string cronSchedule, Func<Task> action)
{
return services.AddCronJob(o => o.Name(name).Distributed(true).CronSchedule(cronSchedule).JobFactory(sp => new DynamicJob(sp, (_, _) => action())));
}

public static IServiceCollection AddDistributedCronJob(this IServiceCollection services, string name, string cronSchedule, Action<IServiceProvider, CancellationToken> action)
{
return services.AddCronJob(o => o.Name(name).Distributed(true).CronSchedule(cronSchedule).JobFactory(sp => new DynamicJob(sp, (xp, ct) =>
{
action(xp, ct);
return Task.CompletedTask;
})));
}

public static IServiceCollection AddDistributedCronJob(this IServiceCollection services, string name, string cronSchedule, Action<CancellationToken> action)
{
return services.AddCronJob(o => o.Name(name).Distributed(true).CronSchedule(cronSchedule).JobFactory(sp => new DynamicJob(sp, (_, ct) =>
{
action(ct);
return Task.CompletedTask;
})));
}

public static IServiceCollection AddDistributedCronJob(this IServiceCollection services, string name, string cronSchedule, Action action)
{
return services.AddCronJob(o => o.Name(name).Distributed(true).CronSchedule(cronSchedule).JobFactory(sp => new DynamicJob(sp, (_, _) =>
{
action();
return Task.CompletedTask;
})));
}

public static IServiceCollection AddJobScheduler(this IServiceCollection services)
{
if (!services.Any(s => s.ServiceType == typeof(IHostedService) && s.ImplementationType == typeof(ScheduledJobService)))
services.AddTransient<IHostedService, ScheduledJobService>();

if (!services.Any(s => s.ServiceType == typeof(IScheduledJobManager) && s.ImplementationType == typeof(ScheduledJobManager)))
services.AddSingleton<IScheduledJobManager, ScheduledJobManager>();

if (!services.Any(s => s.ServiceType == typeof(ScheduledJobManager) && s.ImplementationType == typeof(ScheduledJobManager)))
services.AddSingleton<ScheduledJobManager>();

return services;
}

public static IServiceCollection AddJobLifetimeService(this IServiceCollection services)
Expand Down
6 changes: 0 additions & 6 deletions src/Foundatio.Extensions.Hosting/Jobs/JobOptionsBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -48,12 +48,6 @@ public HostedJobOptionsBuilder RunContinuous(bool value)
return this;
}

public HostedJobOptionsBuilder CronSchedule(string value)
{
Target.CronSchedule = value;
return this;
}

public HostedJobOptionsBuilder Interval(TimeSpan? value)
{
Target.Interval = value;
Expand Down
Loading

0 comments on commit 303146b

Please sign in to comment.