FractalDataWorks.Services.Scheduling
0.9.0-alpha.1011.ged0a6c6e98
This is a prerelease version of FractalDataWorks.Services.Scheduling.
dotnet add package FractalDataWorks.Services.Scheduling --version 0.9.0-alpha.1011.ged0a6c6e98
NuGet\Install-Package FractalDataWorks.Services.Scheduling -Version 0.9.0-alpha.1011.ged0a6c6e98
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="FractalDataWorks.Services.Scheduling" Version="0.9.0-alpha.1011.ged0a6c6e98" />
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="FractalDataWorks.Services.Scheduling" Version="0.9.0-alpha.1011.ged0a6c6e98" />
<PackageReference Include="FractalDataWorks.Services.Scheduling" />
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add FractalDataWorks.Services.Scheduling --version 0.9.0-alpha.1011.ged0a6c6e98
The NuGet Team does not provide support for this client. Please contact its maintainers for support.
#r "nuget: FractalDataWorks.Services.Scheduling, 0.9.0-alpha.1011.ged0a6c6e98"
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package FractalDataWorks.Services.Scheduling@0.9.0-alpha.1011.ged0a6c6e98
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=FractalDataWorks.Services.Scheduling&version=0.9.0-alpha.1011.ged0a6c6e98&prerelease
#tool nuget:?package=FractalDataWorks.Services.Scheduling&version=0.9.0-alpha.1011.ged0a6c6e98&prerelease
The NuGet Team does not provide support for this client. Please contact its maintainers for support.
FractalDataWorks.Services.Scheduling
Overview
The Scheduling framework provides ServiceType auto-discovery for job scheduling and task execution with unified interfaces that work across different scheduling providers and execution engines.
Features
- ServiceType Auto-Discovery: Add scheduling packages and they're automatically registered
- Universal Scheduling Interface: Same API works with all scheduling providers
- Dynamic Scheduler Creation: Scheduling services created via factories
- Source-Generated Collections: High-performance scheduler lookup
Quick Start
1. Install Packages
<ProjectReference Include="..\FractalDataWorks.Services.Scheduling\FractalDataWorks.Services.Scheduling.csproj" />
<ProjectReference Include="..\FractalDataWorks.Services.Scheduling.Quartz\FractalDataWorks.Services.Scheduling.Quartz.csproj" />
2. Register Services
// Program.cs - Zero-configuration registration
builder.Services.AddScoped<IGenericSchedulingProvider, GenericSchedulingProvider>();
// Single line registers ALL discovered scheduling types
SchedulingTypes.Register(builder.Services);
3. Configure Scheduling
{
"Scheduling": {
"QuartzScheduler": {
"SchedulerType": "Quartz",
"MaxConcurrentJobs": 10,
"MisfireThreshold": 60000,
"EnablePersistence": true,
"ConnectionString": "Server=localhost;Database=Quartz;Integrated Security=true;"
}
}
}
4. Use Universal Scheduling
public class ReportService
{
private readonly IGenericSchedulingProvider _schedulingProvider;
public ReportService(IGenericSchedulingProvider schedulingProvider)
{
_schedulingProvider = schedulingProvider;
}
public async Task<IGenericResult> ScheduleDailyReportAsync()
{
var schedulerResult = await _schedulingProvider.GetScheduler("QuartzScheduler");
if (!schedulerResult.IsSuccess)
return GenericResult.Failure(schedulerResult.Error);
using var scheduler = schedulerResult.Value;
// Universal job scheduling - works with any scheduler
var job = new ScheduledJob
{
Name = "DailyReport",
Group = "Reports",
JobType = typeof(DailyReportJob),
Schedule = CronSchedule.Daily(hour: 6, minute: 0), // 6:00 AM daily
Data = new Dictionary<string, object>
{
["ReportType"] = "Daily",
["Recipients"] = new[] { "admin@company.com" }
}
};
var result = await scheduler.ScheduleJobAsync(job);
return result;
}
public async Task<IGenericResult> ScheduleOneTimeTaskAsync(DateTime executeAt)
{
var schedulerResult = await _schedulingProvider.GetScheduler("QuartzScheduler");
if (!schedulerResult.IsSuccess)
return GenericResult.Failure(schedulerResult.Error);
using var scheduler = schedulerResult.Value;
var job = new ScheduledJob
{
Name = $"OneTime_{Guid.NewGuid()}",
Group = "OneTime",
JobType = typeof(CustomTaskJob),
Schedule = SimpleSchedule.At(executeAt),
Data = new Dictionary<string, object>
{
["TaskId"] = Guid.NewGuid(),
["Priority"] = "High"
}
};
return await scheduler.ScheduleJobAsync(job);
}
}
Available Scheduling Types
| Package | Scheduler Type | Purpose |
|---|---|---|
FractalDataWorks.Services.Scheduling.Quartz |
Quartz | Quartz.NET scheduler integration |
FractalDataWorks.Services.Scheduling.Hangfire |
Hangfire | Hangfire background job processor |
FractalDataWorks.Services.Scheduling.Timer |
Timer | Simple timer-based scheduling |
How Auto-Discovery Works
- Source Generator Scans:
[ServiceTypeCollection]attribute triggers compile-time discovery - Finds Implementations: Scans referenced assemblies for types inheriting from
SchedulingTypeBase - Generates Collections: Creates
SchedulingTypes.All,SchedulingTypes.Name(), etc. - Self-Registration: Each scheduling type handles its own DI registration
Adding Custom Scheduling Types
// 1. Create your scheduling type (singleton pattern)
public sealed class CustomSchedulingType : SchedulingTypeBase<IGenericScheduler, CustomSchedulingConfiguration, ICustomSchedulingFactory>
{
// ❌ WRONG - Do NOT add Instance property (old/incorrect pattern)
// public static CustomSchedulingType Instance { get; } = new();
private CustomSchedulingType() : base(4, "Custom", "Scheduling Providers") { }
public override Type FactoryType => typeof(ICustomSchedulingFactory);
public override void Register(IServiceCollection services)
{
services.AddScoped<ICustomSchedulingFactory, CustomSchedulingFactory>();
services.AddScoped<CustomJobStore>();
services.AddScoped<CustomTriggerBuilder>();
}
}
// 2. Add package reference - source generator automatically discovers it
// 3. SchedulingTypes.Register(services) will include it automatically
Common Scheduling Patterns
Recurring Jobs
public class MaintenanceService
{
private readonly IGenericSchedulingProvider _schedulingProvider;
public MaintenanceService(IGenericSchedulingProvider schedulingProvider)
{
_schedulingProvider = schedulingProvider;
}
public async Task<IGenericResult> ScheduleMaintenanceJobsAsync()
{
var schedulerResult = await _schedulingProvider.GetScheduler("QuartzScheduler");
if (!schedulerResult.IsSuccess)
return GenericResult.Failure(schedulerResult.Error);
using var scheduler = schedulerResult.Value;
// Database cleanup - weekly on Sunday at 2 AM
var dbCleanup = new ScheduledJob
{
Name = "DatabaseCleanup",
Group = "Maintenance",
JobType = typeof(DatabaseCleanupJob),
Schedule = CronSchedule.Weekly(DayOfWeek.Sunday, hour: 2, minute: 0)
};
var dbResult = await scheduler.ScheduleJobAsync(dbCleanup);
if (!dbResult.IsSuccess)
return dbResult;
// Log rotation - daily at midnight
var logRotation = new ScheduledJob
{
Name = "LogRotation",
Group = "Maintenance",
JobType = typeof(LogRotationJob),
Schedule = CronSchedule.Daily(hour: 0, minute: 0)
};
return await scheduler.ScheduleJobAsync(logRotation);
}
}
Job Monitoring and Management
public class JobManagementService
{
private readonly IGenericSchedulingProvider _schedulingProvider;
public JobManagementService(IGenericSchedulingProvider schedulingProvider)
{
_schedulingProvider = schedulingProvider;
}
public async Task<IGenericResult<List<JobStatus>>> GetJobStatusAsync()
{
var schedulerResult = await _schedulingProvider.GetScheduler("QuartzScheduler");
if (!schedulerResult.IsSuccess)
return GenericResult<List<JobStatus>>.Failure(schedulerResult.Error);
using var scheduler = schedulerResult.Value;
// Get all scheduled jobs
var jobsResult = await scheduler.GetAllJobsAsync();
if (!jobsResult.IsSuccess)
return GenericResult<List<JobStatus>>.Failure(jobsResult.Error);
var statuses = new List<JobStatus>();
foreach (var job in jobsResult.Value)
{
var statusResult = await scheduler.GetJobStatusAsync(job.Name, job.Group);
if (statusResult.IsSuccess)
statuses.Add(statusResult.Value);
}
return GenericResult<List<JobStatus>>.Success(statuses);
}
public async Task<IGenericResult> PauseJobAsync(string jobName, string jobGroup)
{
var schedulerResult = await _schedulingProvider.GetScheduler("QuartzScheduler");
if (!schedulerResult.IsSuccess)
return GenericResult.Failure(schedulerResult.Error);
using var scheduler = schedulerResult.Value;
return await scheduler.PauseJobAsync(jobName, jobGroup);
}
public async Task<IGenericResult> ResumeJobAsync(string jobName, string jobGroup)
{
var schedulerResult = await _schedulingProvider.GetScheduler("QuartzScheduler");
if (!schedulerResult.IsSuccess)
return GenericResult.Failure(schedulerResult.Error);
using var scheduler = schedulerResult.Value;
return await scheduler.ResumeJobAsync(jobName, jobGroup);
}
}
Dynamic Job Creation
public class DynamicJobService
{
private readonly IGenericSchedulingProvider _schedulingProvider;
public DynamicJobService(IGenericSchedulingProvider schedulingProvider)
{
_schedulingProvider = schedulingProvider;
}
public async Task<IGenericResult> ScheduleDataProcessingJobAsync(int dataSetId, TimeSpan delay)
{
var schedulerResult = await _schedulingProvider.GetScheduler("QuartzScheduler");
if (!schedulerResult.IsSuccess)
return GenericResult.Failure(schedulerResult.Error);
using var scheduler = schedulerResult.Value;
var executeAt = DateTimeOffset.UtcNow.Add(delay);
var job = new ScheduledJob
{
Name = $"DataProcessing_{dataSetId}_{DateTimeOffset.UtcNow.Ticks}",
Group = "DataProcessing",
JobType = typeof(DataProcessingJob),
Schedule = SimpleSchedule.At(executeAt),
Data = new Dictionary<string, object>
{
["DataSetId"] = dataSetId,
["ProcessingMode"] = "Batch",
["MaxRetries"] = 3
}
};
return await scheduler.ScheduleJobAsync(job);
}
}
Architecture Benefits
- Scheduler Agnostic: Switch scheduling providers without code changes
- Zero Configuration: Add package reference, get functionality
- Type Safety: Compile-time validation of scheduling types
- Performance: Source-generated collections use FrozenDictionary
- Scalability: Each scheduling type manages its own job execution strategy
For complete architecture details, see Services.Abstractions README.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.
-
net10.0
- FractalDataWorks.Results (>= 0.9.0-alpha.1011.ged0a6c6e98)
- FractalDataWorks.Services (>= 0.9.0-alpha.1011.ged0a6c6e98)
- FractalDataWorks.Services.Abstractions (>= 0.9.0-alpha.1011.ged0a6c6e98)
- FractalDataWorks.Services.Scheduling.Abstractions (>= 0.9.0-alpha.1011.ged0a6c6e98)
- FractalDataWorks.ServiceTypes (>= 0.9.0-alpha.1011.ged0a6c6e98)
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 10.0.0-rc.2.25502.107)
- Microsoft.Extensions.Logging.Abstractions (>= 10.0.0-rc.2.25502.107)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 0.9.0-alpha.1011.ged0a6c6e98 | 41 | 11/18/2025 |
| 0.9.0-alpha.1010.gecd88aac50 | 42 | 11/18/2025 |
| 0.9.0-alpha.1009.g7f6817e985 | 37 | 11/18/2025 |
| 0.9.0-alpha.1006.gf287016c0c | 38 | 11/18/2025 |
| 0.8.0-alpha.1011 | 36 | 11/18/2025 |
| 0.7.0-alpha.1022 | 128 | 11/3/2025 |
| 0.7.0-alpha.1021 | 131 | 11/3/2025 |
| 0.7.0-alpha.1008 | 104 | 11/2/2025 |
| 0.7.0-alpha.1006 | 127 | 10/30/2025 |
| 0.7.0-alpha.1005 | 130 | 10/30/2025 |
| 0.7.0-alpha.1004 | 126 | 10/30/2025 |
| 0.7.0-alpha.1001 | 138 | 10/29/2025 |
| 0.6.0-alpha.1006 | 132 | 10/29/2025 |
| 0.6.0-alpha.1005 | 128 | 10/28/2025 |
| 0.6.0-alpha.1004 | 129 | 10/28/2025 |