💻 Développement

hangfire-job-scheduler

Planification et gestion de background jobs avec Hangfire en .NET. Patterns de retry, scheduling récurrent, monitoring et bonnes pratiques.

⚡ Installation & lancement en 1 commande

Copiez-collez dans votre terminal : le skill s'installe dans ~/.claude/skills et Claude Code se lance directement dessus.

macOS / Linux
curl -fsSL https://raw.githubusercontent.com/khalilbenaz/claude-skills-collection/main/install.sh | sh -s -- hangfire-job-scheduler --launch
Windows (PowerShell)
iex "& { $(iwr -useb https://raw.githubusercontent.com/khalilbenaz/claude-skills-collection/main/install.ps1) } hangfire-job-scheduler -Launch"

🚀 Déjà installé ?

claude "/hangfire-job-scheduler"

Ou tapez /hangfire-job-scheduler dans une session Claude Code, ou décrivez simplement votre besoin — le skill se déclenche automatiquement via le skill-router.

🔑 Déclencheurs automatiques

Le skill s'active automatiquement quand votre demande contient :

hangfirebackground jobtâche de fond .NETjob récurrentcron job C#fire and forget

📦 Installation manuelle

git clone https://github.com/khalilbenaz/claude-skills-collection.git cp -r claude-skills-collection/dev-skills/hangfire-job-scheduler ~/.claude/skills/

Source : dev-skills/hangfire-job-scheduler

📖 Manuel

Hangfire Job Scheduler

Workflow

  1. Identifier le type de job : fire-and-forget, delayed, récurrent ou continuation.
  2. Concevoir le job : définir la classe, les paramètres et la logique de retry.
  3. Configurer : storage, dashboard, queues et options de sérialisation.
  4. Monitorer : dashboard Hangfire, alertes sur les échecs.

Types de jobs

TypeUsageAPI
Fire-and-forgetExécution immédiate en arrière-planBackgroundJob.Enqueue
DelayedExécution différéeBackgroundJob.Schedule
RecurringPlanifié (CRON)RecurringJob.AddOrUpdate
ContinuationChaînage après un autre jobBackgroundJob.ContinueJobWith
BatchGroupe de jobs (Hangfire Pro)BatchJob.StartNew

Configuration de base

// Program.cs
builder.Services.AddHangfire(config => config
    .SetDataCompatibilityLevel(CompatibilityLevel.Version_180)
    .UseSimpleAssemblyNameTypeSerializer()
    .UseRecommendedSerializerSettings()
    .UseSqlServerStorage(connectionString, new SqlServerStorageOptions
    {
        CommandBatchMaxTimeout = TimeSpan.FromMinutes(5),
        SlidingInvisibilityTimeout = TimeSpan.FromMinutes(5),
        QueuePollInterval = TimeSpan.FromSeconds(15),
        UseRecommendedIsolationLevel = true,
        SchemaName = "hangfire"
    }));

builder.Services.AddHangfireServer(options =>
{
    options.Queues = new[] { "critical", "default", "low" };
    options.WorkerCount = Environment.ProcessorCount * 2;
});

// Dashboard (protéger en production !)
app.UseHangfireDashboard("/hangfire", new DashboardOptions
{
    Authorization = new[] { new HangfireAuthorizationFilter() }
});

Conception de jobs

Structure recommandée

public interface IEmailJobService
{
    Task SendWelcomeEmail(string userId);
    Task SendMonthlyReport(string userId, int month, int year);
}

public class EmailJobService : IEmailJobService
{
    private readonly IEmailSender _sender;
    private readonly IUserRepository _users;

    public EmailJobService(IEmailSender sender, IUserRepository users)
    {
        _sender = sender;
        _users = users;
    }

    [AutomaticRetry(Attempts = 3, DelaysInSeconds = new[] { 60, 300, 900 })]
    [Queue("default")]
    public async Task SendWelcomeEmail(string userId)
    {
        var user = await _users.GetByIdAsync(userId)
            ?? throw new InvalidOperationException($"User {userId} not found");

        await _sender.SendAsync(user.Email, "Bienvenue", "welcome-template");
    }

    [AutomaticRetry(Attempts = 5)]
    [Queue("low")]
    public async Task SendMonthlyReport(string userId, int month, int year)
    {
        // Job longue durée — vérifier l'annulation
        var user = await _users.GetByIdAsync(userId);
        var report = await GenerateReport(user, month, year);
        await _sender.SendAsync(user.Email, $"Rapport {month}/{year}", report);
    }
}

Enregistrement des jobs

// Fire-and-forget
BackgroundJob.Enqueue<IEmailJobService>(x => x.SendWelcomeEmail(userId));

// Delayed (dans 24h)
BackgroundJob.Schedule<IEmailJobService>(
    x => x.SendWelcomeEmail(userId),
    TimeSpan.FromHours(24));

// Récurrent (tous les jours à 8h)
RecurringJob.AddOrUpdate<IEmailJobService>(
    "daily-report",
    x => x.SendMonthlyReport(userId, DateTime.Now.Month, DateTime.Now.Year),
    "0 8 * * *",
    new RecurringJobOptions { TimeZone = TimeZoneInfo.FindSystemTimeZoneById("Europe/Paris") });

// Continuation
var parentId = BackgroundJob.Enqueue<IDataService>(x => x.ImportData());
BackgroundJob.ContinueJobWith<INotificationService>(
    parentId,
    x => x.NotifyImportComplete());

Patterns de retry

Stratégie exponentielle

[AutomaticRetry(
    Attempts = 5,
    DelaysInSeconds = new[] { 30, 120, 600, 3600, 14400 },
    OnAttemptsExceeded = AttemptsExceededAction.Delete)]
public async Task ProcessPayment(string paymentId) { /* ... */ }

Gestion des erreurs

// Filtrer les erreurs non-retryable
public class SmartRetryAttribute : JobFilterAttribute, IElectStateFilter
{
    public void OnStateElection(ElectStateContext context)
    {
        if (context.CandidateState is FailedState failedState)
        {
            // Ne pas retry les erreurs métier
            if (failedState.Exception is BusinessException)
            {
                context.CandidateState = new DeletedState();
            }
        }
    }
}

Bonnes pratiques

À faire

À éviter

Règles