Background Services

Back

Loading concept...

🏭 Background Services in ASP.NET

Your App’s Invisible Helpers That Never Sleep


The Factory Analogy 🏢

Imagine your ASP.NET app is a busy factory. When customers visit (HTTP requests), workers help them right away. But some jobs need to happen behind the scenes—cleaning floors at night, checking inventory, sending reminder emails.

These behind-the-scenes workers are Background Services. They work quietly, never facing customers, but keeping everything running smoothly.


🎯 What You’ll Learn

graph LR A["Background Services"] --> B["Hosted Services"] A --> C["IHostedService"] A --> D["BackgroundService Class"] A --> E["Service Lifecycle"] A --> F["Worker Services"] A --> G["Timed Tasks"] A --> H["Scoped Services"]

1️⃣ Hosted Services: The Night Shift Workers

What Are They?

A Hosted Service is any background worker that runs alongside your main app. Think of it like a janitor who starts work when the factory opens and leaves when it closes.

Simple Example

public class CleanupService
    : IHostedService
{
    public Task StartAsync(
        CancellationToken ct)
    {
        Console.WriteLine(
            "Cleanup started!");
        return Task.CompletedTask;
    }

    public Task StopAsync(
        CancellationToken ct)
    {
        Console.WriteLine(
            "Cleanup stopped!");
        return Task.CompletedTask;
    }
}

Register It

// In Program.cs
builder.Services
    .AddHostedService<CleanupService>();

Real Life: Email sender, cache warmer, health checker—all hosted services!


2️⃣ IHostedService: The Contract

The Promise Every Background Worker Makes

IHostedService is like a job description. Every background worker must:

  1. Start when the app starts (StartAsync)
  2. Stop gracefully when the app shuts down (StopAsync)
graph TD A["App Starts"] --> B["StartAsync Called"] B --> C["Worker Runs..."] C --> D["App Shutting Down"] D --> E["StopAsync Called"] E --> F["Clean Exit"]

The Interface

public interface IHostedService
{
    Task StartAsync(
        CancellationToken ct);

    Task StopAsync(
        CancellationToken ct);
}

Think of it like: A promise to show up on time and leave properly—no ghosting!


3️⃣ BackgroundService Class: The Easy Button

Why Reinvent the Wheel?

Writing IHostedService from scratch gets repetitive. The BackgroundService class gives you a shortcut!

Instead of implementing both methods, just override one: ExecuteAsync.

Before (Manual Way)

public class MyWorker : IHostedService
{
    private Task? _task;

    public Task StartAsync(
        CancellationToken ct)
    {
        _task = DoWorkAsync(ct);
        return Task.CompletedTask;
    }

    public async Task StopAsync(
        CancellationToken ct)
    {
        if (_task != null)
            await _task;
    }

    private async Task DoWorkAsync(
        CancellationToken ct) { }
}

After (Easy Way) ✨

public class MyWorker
    : BackgroundService
{
    protected override async Task
        ExecuteAsync(
            CancellationToken ct)
    {
        while (!ct.IsCancellationRequested)
        {
            // Do your work here!
            await Task.Delay(1000, ct);
        }
    }
}

Magic! Less code, same power. The BackgroundService handles start/stop for you.


4️⃣ Hosted Service Lifecycle: Birth to Goodbye

The Journey of a Background Worker

graph TD A["🌱 Created"] --> B["📦 Registered"] B --> C["🚀 StartAsync"] C --> D["⚡ Running"] D --> E["🛑 StopAsync"] E --> F["🪦 Disposed"]

Key Moments

Phase What Happens
Created Object is born
StartAsync Worker begins
Running Doing its job
StopAsync Graceful exit
Disposed Memory freed

Important Rule

⚠️ StartAsync must return quickly! Don’t block—start your long-running work and return.

// ✅ Good: Return immediately
public Task StartAsync(
    CancellationToken ct)
{
    _task = RunAsync(ct);
    return Task.CompletedTask;
}

// ❌ Bad: Blocking forever
public Task StartAsync(
    CancellationToken ct)
{
    while (true) { }  // NEVER DO THIS!
}

5️⃣ Worker Services: Standalone Background Apps

What If Background Work Is ALL You Need?

Sometimes you don’t need a web server—just a program that runs tasks forever. That’s a Worker Service!

Create One

dotnet new worker -n MyWorker

What You Get

public class Worker : BackgroundService
{
    private readonly ILogger<Worker>
        _logger;

    public Worker(ILogger<Worker> logger)
    {
        _logger = logger;
    }

    protected override async Task
        ExecuteAsync(
            CancellationToken ct)
    {
        while (!ct.IsCancellationRequested)
        {
            _logger.LogInformation(
                "Working at: {time}",
                DateTimeOffset.Now);

            await Task.Delay(1000, ct);
        }
    }
}

Real Uses

  • 📧 Process email queues
  • 📊 Sync data between systems
  • 🔍 Monitor files for changes
  • 📨 Handle message queues

6️⃣ Timed Background Tasks: The Alarm Clock

Running Tasks on a Schedule

Want something to run every 5 minutes? Every hour? Use a timer!

public class TimedService
    : BackgroundService
{
    private readonly TimeSpan _period =
        TimeSpan.FromMinutes(5);

    protected override async Task
        ExecuteAsync(
            CancellationToken ct)
    {
        using var timer =
            new PeriodicTimer(_period);

        while (!ct.IsCancellationRequested &&
               await timer
                   .WaitForNextTickAsync(ct))
        {
            await DoScheduledWork();
        }
    }

    private Task DoScheduledWork()
    {
        Console.WriteLine(
            quot;Task ran at {DateTime.Now}");
        return Task.CompletedTask;
    }
}

Timer Options

Method Best For
PeriodicTimer .NET 6+, clean async
Timer Older .NET, callbacks
Task.Delay Simple delays

7️⃣ Scoped Services in Background: The Tricky Part

The Problem

Background services are singletons—they live forever. But some services (like database contexts) are scoped—they live per-request.

You can’t inject scoped services directly into singletons!

// ❌ This will CRASH!
public class BadWorker : BackgroundService
{
    private readonly MyDbContext _db;

    public BadWorker(MyDbContext db)
    {
        _db = db;  // BOOM! Scope error!
    }
}

The Solution: Create a Scope

Inject IServiceScopeFactory and create scopes manually:

public class GoodWorker
    : BackgroundService
{
    private readonly
        IServiceScopeFactory _scopeFactory;

    public GoodWorker(
        IServiceScopeFactory factory)
    {
        _scopeFactory = factory;
    }

    protected override async Task
        ExecuteAsync(
            CancellationToken ct)
    {
        while (!ct.IsCancellationRequested)
        {
            using (var scope =
                _scopeFactory.CreateScope())
            {
                var db = scope
                    .ServiceProvider
                    .GetRequiredService
                        <MyDbContext>();

                // Now use db safely!
                await db.SaveChangesAsync();
            }

            await Task.Delay(5000, ct);
        }
    }
}
graph TD A["Background Service"] --> B["Create Scope"] B --> C["Get Scoped Service"] C --> D["Do Work"] D --> E["Dispose Scope"] E --> F["Wait"] F --> B

🎁 Quick Reference

Concept One-Liner
Hosted Service Background worker tied to app lifetime
IHostedService Interface with StartAsync + StopAsync
BackgroundService Base class with just ExecuteAsync
Lifecycle Create → Start → Run → Stop → Dispose
Worker Service Standalone app for background work
Timed Tasks Use PeriodicTimer for schedules
Scoped Services Use IServiceScopeFactory to create scopes

🚀 You Did It!

You now understand how ASP.NET apps run tasks in the background! Like invisible factory workers, these services keep your app healthy, send emails, process queues, and much more—all without users ever seeing them.

Remember: Background services are your app’s silent heroes. Treat them well! 🦸‍♀️

Loading story...

Story - Premium Content

Please sign in to view this story and start learning.

Upgrade to Premium to unlock full access to all stories.

Stay Tuned!

Story is coming soon.

Story Preview

Story - Premium Content

Please sign in to view this concept and start learning.

Upgrade to Premium to unlock full access to all content.