Shaunebu.Common.Scheduler
1.0.0
dotnet add package Shaunebu.Common.Scheduler --version 1.0.0
NuGet\Install-Package Shaunebu.Common.Scheduler -Version 1.0.0
<PackageReference Include="Shaunebu.Common.Scheduler" Version="1.0.0" />
<PackageVersion Include="Shaunebu.Common.Scheduler" Version="1.0.0" />
<PackageReference Include="Shaunebu.Common.Scheduler" />
paket add Shaunebu.Common.Scheduler --version 1.0.0
#r "nuget: Shaunebu.Common.Scheduler, 1.0.0"
#:package Shaunebu.Common.Scheduler@1.0.0
#addin nuget:?package=Shaunebu.Common.Scheduler&version=1.0.0
#tool nuget:?package=Shaunebu.Common.Scheduler&version=1.0.0
Shaunebu.Common.Scheduler ⏰🔄
Overview ✨
Shaunebu.Common.Scheduler is a high-performance, enterprise-grade scheduling library for .NET applications that provides advanced job scheduling, retry policies, distributed locking, and comprehensive monitoring. Built for modern cloud-native applications with support for complex scheduling scenarios.
##Feature Comparison 🆚
| Feature | Hangfire | Quartz.NET | Shaunebu.Scheduler | Benefit |
|---|---|---|---|---|
| Setup Complexity 🏗️ | Medium | High | ✅ Low | Faster time to production |
| Retry Policies 🔄 | Basic | Basic | ✅ Advanced | Exponential backoff, custom strategies |
| Distributed Locks 🔒 | ❌ Limited | ✅ With setup | ✅ Built-in | Multi-instance support out-of-the-box |
| Job History 📊 | ✅ Basic | ❌ Manual | ✅ Comprehensive | Built-in analytics and metrics |
| Plugin System 🔌 | ❌ | ✅ Limited | ✅ Extensible | Custom monitoring and auditing |
| MAUI Support 📱 | ❌ | ❌ | ✅ Native | Mobile and desktop optimized |
| Performance 🚀 | Good | Good | ✅ Excellent | Optimized for high throughput |
| Dependencies 📦 | Heavy | Moderate | ✅ Lightweight | Faster startup, smaller footprint |
Installation 📦
dotnet add package Shaunebu.Common.Scheduler
Quick Start 🚀
1. Basic Setup
// In MauiProgram.cs or Startup
builder.Services.AddShaunebuScheduler(options =>
{
options.MaxConcurrentJobs = 5;
options.ShutdownTimeout = TimeSpan.FromSeconds(30);
options.ContinueOnFailure = true;
})
.AddConsole()
.AddSchedulerPlugins();
2. Simple Job Scheduling
public class WeatherService
{
private readonly IScheduler _scheduler;
public WeatherService(IScheduler scheduler)
{
_scheduler = scheduler;
// Schedule weather data refresh every 30 minutes
_scheduler.ScheduleJob("WeatherRefresh", async (ct) =>
{
await RefreshWeatherDataAsync();
}, 30.EveryMinutes());
}
}
Core Features 🎯
1. Multiple Trigger Types 🎪
// Cron expressions
_scheduler.ScheduleJob("CronJob", DoWork, "0 */2 * * *".Cron());
// Simple intervals
_scheduler.ScheduleJob("IntervalJob", DoWork, 15.EveryMinutes());
// Daily at specific time
_scheduler.ScheduleJob("DailyJob", DoWork, 9.Hours().DailyAt());
// Business days only (skip weekends/holidays)
_scheduler.ScheduleJob("BusinessJob", DoWork,
new BusinessDayTrigger(TimeSpan.FromHours(9)));
// One-time execution
_scheduler.ScheduleJob("OneTimeJob", DoWork,
DateTimeOffset.Now.AddHours(2).RunOnceAt());
2. Advanced Retry Policies 🔄
_scheduler.ScheduleJob("PaymentProcessing", async (ct) =>
{
await ProcessPaymentAsync();
}, 5.EveryMinutes(), new RetryPolicy
{
MaxRetries = 5,
BackoffStrategy = RetryBackoffStrategy.Exponential,
InitialDelay = TimeSpan.FromSeconds(1),
MaxDelaySeconds = 300,
ShouldRetry = ex => ex is not BusinessException
});
3. Stateful Jobs 🏢
public class ReportGenerationState
{
public int ReportId { get; set; }
public string Format { get; set; }
public int AttemptCount { get; set; }
}
var state = new ReportGenerationState { ReportId = 123, Format = "PDF" };
_scheduler.ScheduleJob("ReportJob", state, async (reportState, ct) =>
{
reportState.AttemptCount++;
await GenerateReportAsync(reportState.ReportId, reportState.Format);
}, 1.EveryHours());
4. Distributed Locking 🔒
// Automatic distributed locking for multi-instance deployments
_scheduler.ScheduleJob("ClusterSafeJob", async (ct) =>
{
// This job will only run on one instance in a cluster
await PerformClusterSafeOperationAsync();
}, 10.EveryMinutes());
5. Comprehensive Monitoring 📊
// Get job analytics
var analytics = await _scheduler.GetJobAnalyticsAsync(
jobId,
DateTime.Today.AddDays(-7),
DateTime.Today);
Console.WriteLine($"Success Rate: {analytics.SuccessRate:P2}");
Console.WriteLine($"Average Duration: {analytics.AverageDuration:mm\\:ss}");
Console.WriteLine($"Total Executions: {analytics.TotalExecutions}");
Enterprise Features 🏢
1. Plugin System 🔌
services.AddShaunebuScheduler()
.AddSchedulerPlugins(options =>
{
options.EnableAuditPlugin = true; // Log all job executions
options.EnableMetricsPlugin = true; // Collect performance metrics
options.CustomPlugins = [typeof(MyCustomPlugin)];
});
// Custom plugin example
public class SecurityAuditPlugin : ISchedulerPlugin
{
public Task OnJobExecutingAsync(IScheduledJob job)
{
_auditService.LogJobStart(job.Id, job.Name, DateTime.UtcNow);
return Task.CompletedTask;
}
}
2. Alerting System 🚨
// Register alert providers
services.AddShaunebuScheduler()
.WithEmailAlerts(options =>
{
options.SmtpServer = "smtp.company.com";
options.ToAddresses = ["team@company.com"];
})
.WithSlackAlerts("https://hooks.slack.com/...");
// Alerts are automatically sent for:
// - Job failures after retries exhausted
// - Unexpected execution errors
// - System health issues
3. Bulk Operations 📚
// Bulk schedule multiple jobs
var jobs = new[]
{
new JobDefinition { Name = "Job1", Trigger = "0 * * * *".Cron() },
new JobDefinition { Name = "Job2", Trigger = 30.EveryMinutes() }
};
await _scheduler.BulkScheduleAsync(jobs);
// Bulk management
await _scheduler.BulkPauseAsync(["Job1", "Job2"]);
await _scheduler.BulkResumeAsync(["Job1"]);
await _scheduler.BulkUnscheduleAsync(["Job2"]);
4. Persistence & Recovery 💾
// SQL Server persistence
services.AddShaunebuScheduler()
.WithSqlPersistence(connectionString);
// Jobs survive application restarts
// Execution history is preserved
// Failed jobs can be analyzed and retried
Advanced Usage 🛠️
1. Complex Workflow Scheduling
// Chain jobs: Job2 runs after Job1 completes
var job1 = _scheduler.ScheduleJob("DataExtraction", ExtractData, 9.EveryHours());
var job2 = _scheduler.ScheduleJob("DataProcessing", ProcessData,
new ChainedTrigger(job1, _scheduler));
// Conditional scheduling based on events
var eventTrigger = new EventTrigger("DataReady", _eventBus);
_scheduler.ScheduleJob("EventDrivenJob", ProcessReadyData, eventTrigger);
2. Custom Triggers
public class MarketHoursTrigger : IScheduleTrigger
{
public DateTimeOffset? GetNextOccurrence(DateTimeOffset from)
{
// Only run during market hours (9:30 AM - 4:00 PM ET)
var easternTime = TimeZoneInfo.ConvertTime(from, TimeZoneInfo.FindSystemTimeZoneById("Eastern Standard Time"));
var nextMarketOpen = GetNextMarketOpen(easternTime);
return new DateTimeOffset(nextMarketOpen, TimeZoneInfo.FindSystemTimeZoneById("Eastern Standard Time").GetUtcOffset(nextMarketOpen));
}
}
_scheduler.ScheduleJob("MarketDataJob", CollectMarketData, new MarketHoursTrigger());
3. Health Monitoring Integration
public class SchedulerHealthCheck : IHealthCheck
{
private readonly IScheduler _scheduler;
public async Task<HealthCheckResult> CheckHealthAsync(HealthCheckContext context, CancellationToken cancellationToken = default)
{
var jobs = _scheduler.GetScheduledJobs();
var failedJobs = jobs.Count(j => j.Status == JobStatus.Failed);
return failedJobs > 0
? HealthCheckResult.Degraded($"{failedJobs} jobs in failed state")
: HealthCheckResult.Healthy("All jobs running normally");
}
}
MAUI Integration 📱
1. Lifecycle-Aware Scheduling
public partial class App : Application
{
private readonly IScheduler _scheduler;
public App(IScheduler scheduler)
{
_scheduler = scheduler;
InitializeComponent();
MainPage = new MainPage();
}
protected override async void OnStart()
{
await _scheduler.StartAsync();
}
protected override async void OnSleep()
{
await _scheduler.StopAsync();
}
protected override async void OnResume()
{
await _scheduler.StartAsync();
}
}
2. Background Tasks in MAUI
public class LocationTrackingService
{
private readonly IScheduler _scheduler;
public LocationTrackingService(IScheduler scheduler)
{
_scheduler = scheduler;
// Schedule location updates every 5 minutes
_scheduler.ScheduleJob("LocationUpdate", async (ct) =>
{
var location = await GetCurrentLocationAsync();
await SaveLocationAsync(location);
}, 5.EveryMinutes(), new RetryPolicy { MaxRetries = 3 });
}
}
Performance Optimization 🚀
1. Configuration Tuning
services.AddShaunebuScheduler(options =>
{
options.MaxConcurrentJobs = Environment.ProcessorCount * 2;
options.JobExecutionTimeout = TimeSpan.FromMinutes(10);
options.ShutdownTimeout = TimeSpan.FromSeconds(60);
options.ContinueOnFailure = false; // Stop on failure for critical jobs
});
2. Memory Management
// For long-running applications, configure history retention
services.AddShaunebuScheduler()
.WithSqlPersistence(connectionString)
.WithHistoryRetention(TimeSpan.FromDays(30)); // Keep 30 days of history
3. Monitoring and Metrics
// Integrate with application metrics
services.AddShaunebuScheduler()
.WithApplicationInsights()
.WithPrometheusMetrics();
// Custom metrics collection
services.AddSingleton<ISchedulerPlugin, CustomMetricsPlugin>();
Best Practices 📝
✅ DO:
// Use descriptive job names
_scheduler.ScheduleJob("Customer_Email_Campaign_Processing", ...);
// Implement proper error handling
_scheduler.ScheduleJob("PaymentProcessing", async (ct) =>
{
try
{
await ProcessPaymentsAsync();
}
catch (PaymentGatewayException ex)
{
_logger.LogError(ex, "Payment gateway unavailable");
throw; // Let retry policy handle it
}
}, 5.EveryMinutes());
// Use appropriate retry policies for external dependencies
new RetryPolicy
{
MaxRetries = 5,
BackoffStrategy = RetryBackoffStrategy.Exponential,
ShouldRetry = ex => ex is TransientException
};
❌ DON'T:
// Don't use generic job names
_scheduler.ScheduleJob("Job1", ...); // ❌
// Don't ignore exceptions
_scheduler.ScheduleJob("DataSync", async (ct) =>
{
await SyncDataAsync(); // ❌ Exceptions are swallowed
});
// Don't schedule too frequently for resource-intensive jobs
_scheduler.ScheduleJob("ReportGeneration", GenerateReport, 10.EverySeconds()); // ❌
Troubleshooting 🔧
Common Issues
Jobs not executing?
Check
MinimumLevelconfigurationVerify trigger configuration
Check concurrent job limits
Performance issues?
Reduce batch size for memory-constrained environments
Increase batch interval for battery-sensitive applications
Use appropriate retry policies
Memory leaks?
Ensure proper job disposal
Configure history retention policies
Monitor job execution durations
Debugging Configuration
// Enable detailed logging for troubleshooting
services.AddShaunebuScheduler(options =>
{
options.LogJobExecutions = true;
})
.AddSchedulerPlugins(options =>
{
options.EnableAuditPlugin = true; // Detailed execution logging
});
// Check scheduler status
var jobs = _scheduler.GetScheduledJobs();
_logger.LogInformation("Active jobs: {Count}", jobs.Count);
foreach (var job in jobs)
{
_logger.LogInformation("Job: {Name}, Status: {Status}, Next: {NextRun}",
job.Name, job.Status, job.NextRun);
}
API Reference 📚
IScheduler Interface
| Method | Description |
|---|---|
ScheduleJob(name, job, trigger) |
Schedule stateless job |
ScheduleJob(name, state, job, trigger) |
Schedule stateful job |
ScheduleJob(name, job, trigger, retryPolicy) |
Schedule with retry policy |
UnscheduleJob(jobId) |
Remove scheduled job |
GetScheduledJobs() |
Get all scheduled jobs |
StartAsync() |
Start scheduler |
StopAsync() |
Stop scheduler |
GetJobAnalyticsAsync(jobId, from, to) |
Get job execution analytics |
TriggerJobAsync(jobId) |
Manually trigger job |
BulkScheduleAsync(jobs) |
Bulk schedule multiple jobs |
BulkUnscheduleAsync(jobIds) |
Bulk remove jobs |
Trigger Extensions
| Method | Description |
|---|---|
EverySeconds(seconds) |
Interval trigger in seconds |
EveryMinutes(minutes) |
Interval trigger in minutes |
EveryHours(hours) |
Interval trigger in hours |
Cron(expression) |
Cron expression trigger |
DailyAt(time) |
Daily at specific time |
RunOnceAt(time) |
One-time execution |
RunOnceIn(delay) |
One-time execution after delay |
Configuration Options
| Option | Default | Description |
|---|---|---|
MaxConcurrentJobs |
5 | Maximum simultaneous job executions |
ShutdownTimeout |
30s | Graceful shutdown timeout |
JobExecutionTimeout |
30m | Maximum job execution time |
ContinueOnFailure |
true | Continue scheduling failed jobs |
LogJobExecutions |
true | Enable execution logging |
Migration Guide 🚚
From Quartz.NET
// QUARTZ.NET
var job = JobBuilder.Create<MyJob>()
.WithIdentity("myJob", "group1")
.Build();
var trigger = TriggerBuilder.Create()
.WithIdentity("myTrigger", "group1")
.WithCronSchedule("0 0/5 * * * ?")
.Build();
scheduler.ScheduleJob(job, trigger);
// SHAUNEBU SCHEDULER
_scheduler.ScheduleJob("MyJob", async (ct) =>
{
await MyJob.Execute();
}, "0 0/5 * * * ?".Cron());
From Hangfire
// HANGFIRE
RecurringJob.AddOrUpdate<MyService>(
"my-job",
x => x.DoWork(),
"*/5 * * * *");
// SHAUNEBU SCHEDULER
_scheduler.ScheduleJob("MyJob", async (ct) =>
{
await myService.DoWork();
}, "*/5 * * * *".Cron());
Examples 🎨
E-commerce Order Processing
public class OrderProcessingService
{
private readonly IScheduler _scheduler;
public OrderProcessingService(IScheduler scheduler)
{
_scheduler = scheduler;
// Process abandoned carts every hour
_scheduler.ScheduleJob("AbandonedCartProcessing", async (ct) =>
{
var abandonedCarts = await _cartService.GetAbandonedCartsAsync();
foreach (var cart in abandonedCarts)
{
await _emailService.SendReminderAsync(cart);
}
}, 1.EveryHours(), new RetryPolicy { MaxRetries = 3 });
// Clean up old orders daily at 2 AM
_scheduler.ScheduleJob("OrderCleanup", async (ct) =>
{
await _orderService.CleanupOldOrdersAsync();
}, 2.Hours().DailyAt());
}
}
IoT Data Collection
public class IoTDataService
{
public IoTDataService(IScheduler scheduler)
{
// Collect sensor data every 30 seconds with aggressive retry
_scheduler.ScheduleJob("SensorDataCollection", async (ct) =>
{
var sensorReadings = await _sensorNetwork.ReadAllSensorsAsync();
await _dataLake.StoreReadingsAsync(sensorReadings);
}, 30.EverySeconds(), new RetryPolicy
{
MaxRetries = 10,
BackoffStrategy = RetryBackoffStrategy.Linear,
InitialDelay = TimeSpan.FromSeconds(1)
});
// Health check every 5 minutes
_scheduler.ScheduleJob("DeviceHealthCheck", async (ct) =>
{
var healthStatus = await _sensorNetwork.CheckHealthAsync();
if (!healthStatus.IsHealthy)
{
await _alertService.SendAlertAsync(healthStatus);
}
}, 5.EveryMinutes());
}
}
Financial Reporting
public class FinancialReportService
{
public FinancialReportService(IScheduler scheduler)
{
// End-of-day reports at 6 PM on business days
_scheduler.ScheduleJob("EODReporting", async (ct) =>
{
var report = await _reportGenerator.GenerateEODReportAsync();
await _distributionService.DistributeReportAsync(report);
}, new BusinessDayTrigger(TimeSpan.FromHours(18)));
// Monthly reconciliation on first business day of month
_scheduler.ScheduleJob("MonthlyReconciliation", async (ct) =>
{
await _accountingService.ReconcileAccountsAsync();
}, "0 0 1 * *".Cron()); // 1st day of month at midnight
}
}
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net9.0 is compatible. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. 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. |
-
net10.0
- Microsoft.Extensions.DependencyInjection (>= 9.0.10)
- Microsoft.Extensions.Hosting (>= 9.0.10)
- Microsoft.Extensions.Logging.Abstractions (>= 9.0.10)
- NCrontab (>= 3.4.0)
-
net9.0
- Microsoft.Extensions.DependencyInjection (>= 9.0.10)
- Microsoft.Extensions.Hosting (>= 9.0.10)
- Microsoft.Extensions.Logging.Abstractions (>= 9.0.10)
- NCrontab (>= 3.4.0)
NuGet packages (1)
Showing the top 1 NuGet packages that depend on Shaunebu.Common.Scheduler:
| Package | Downloads |
|---|---|
|
Shaunebu.Bussiness.ReportGenerator.Scheduler
Cron-based scheduling module for Shaunebu ReportGenerator. Automate report generation with tenant awareness. |
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 1.0.0 | 302 | 11/11/2025 |