Why IHostedService Exists in ASP.NET Core | BackgroundService vs Timer Explained

Why IHostedService Exists in ASP.NET Core | BackgroundService vs Timer Explained

Why IHostedService Exists in ASP.NET Core

A typical web application follows the request–response model. A client sends a request, the server processes the request, and then returns a response.

So naturally we might think that if there is no request, no code will execute.

However many real-world applications need to run tasks continuously in the background.

Examples of Background Tasks

  • Sending emails
  • Processing uploaded files
  • Collecting analytics data
  • Refreshing cache
  • Running scheduled jobs

ASP.NET Core provides the IHostedService interface to support such scenarios.


The IHostedService Interface


public interface IHostedService
{
    Task StartAsync(CancellationToken cancellationToken);
    Task StopAsync(CancellationToken cancellationToken);
}

StartAsync runs when the application starts and StopAsync runs when the application stops.


Registering a Hosted Service

Add the hosted service inside Program.cs:


builder.Services.AddHostedService<ApplicationLifetimeService>();

1. Simple IHostedService Example


public class ApplicationLifetimeService : IHostedService
{
    private readonly IHostApplicationLifetime _appLifetime;

    public ApplicationLifetimeService(IHostApplicationLifetime appLifetime)
    {
        _appLifetime = appLifetime;
    }

    public Task StartAsync(CancellationToken cancellationToken)
    {
        _appLifetime.ApplicationStopping.Register(OnStopping);
        _appLifetime.ApplicationStopped.Register(OnStopped);

        return Task.CompletedTask;
    }

    public Task StopAsync(CancellationToken cancellationToken)
    {
        return Task.CompletedTask;
    }

    private void OnStopping()
    {
        Console.WriteLine("ApplicationStopping event fired. Saving data...");
    }

    private void OnStopped()
    {
        Console.WriteLine("ApplicationStopped event fired.");
    }
}

2. Boilerplate Lifecycle Example


public class ApplicationLifetimeService : IHostedService
{
    private readonly IHostApplicationLifetime _appLifetime;
    private readonly ILogger<ApplicationLifetimeService> _logger;

    public ApplicationLifetimeService(
        IHostApplicationLifetime appLifetime,
        ILogger<ApplicationLifetimeService> logger)
    {
        _appLifetime = appLifetime;
        _logger = logger;
    }

    public Task StartAsync(CancellationToken cancellationToken)
    {
        _logger.LogInformation("ApplicationLifetimeService starting.");

        _appLifetime.ApplicationStarted.Register(OnStarted);
        _appLifetime.ApplicationStopping.Register(OnStopping);
        _appLifetime.ApplicationStopped.Register(OnStopped);

        return Task.CompletedTask;
    }

    public Task StopAsync(CancellationToken cancellationToken)
    {
        _logger.LogInformation("StopAsync triggered.");
        return Task.CompletedTask;
    }

    private void OnStarted()
    {
        _logger.LogInformation("Application started.");
    }

    private void OnStopping()
    {
        _logger.LogInformation("Application stopping.");
    }

    private void OnStopped()
    {
        _logger.LogInformation("Application stopped.");
    }
}

3. Running Background Tasks Using Timer

A common approach is using System.Threading.Timer.


_timer = new Timer(DoWork, null, TimeSpan.Zero, TimeSpan.FromSeconds(5));

However timers run on the .NET ThreadPool. If the previous execution has not finished, another thread may start executing the callback.

This can cause thread-safety problems.


Thread Safe Version Using ConcurrentDictionary


private readonly ConcurrentDictionary<string,int> _requestStats = new();

private void DoWork(object? state)
{
    string[] keys =
    {
        "India","Nepal","Bhutan","Sri-Lanka","Japan",
        "USA","UK","Canada","Australia","New-Zealand","Netherlands"
    };

    string key = keys[_random.Next(keys.Length)];

    _requestStats.AddOrUpdate(
        key,
        1,
        (k, oldValue) => oldValue + 1);

    _logger.LogInformation($"Stats updated for {key}");
}

Important Note About Shutdown

To allow ASP.NET Core to run cleanup logic, the application should be stopped using Ctrl + C.

Stopping directly from Visual Studio kills the process immediately and lifecycle events may not execute.


Final Approach: Using BackgroundService

ASP.NET Core provides a cleaner approach using BackgroundService.


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

    private readonly Dictionary<string, int> _requestStats = new();

    private readonly Random _random = new Random();

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

    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        _logger.LogInformation("Background service started.");

        while (!stoppingToken.IsCancellationRequested)
        {
            DoWork();

            try
            {
                await Task.Delay(5000, stoppingToken);
            }
            catch (TaskCanceledException)
            {
            }
        }

        _logger.LogInformation("Background service stopping.");

        SaveDataToDisk();
    }

    private void DoWork()
    {
        string[] keys =
        {
            "India","Nepal","Bhutan","Sri-Lanka","Japan",
            "USA","UK","Canada","Australia","New-Zealand","Netherlands"
        };

        string key = keys[_random.Next(keys.Length)];

        if (_requestStats.ContainsKey(key))
            _requestStats[key]++;
        else
            _requestStats[key] = 1;

        _logger.LogInformation($"Stats updated for {key}");
    }

    private void SaveDataToDisk()
    {
        string json = JsonSerializer.Serialize(_requestStats, new JsonSerializerOptions
        {
            WriteIndented = true
        });

        File.WriteAllText("requestStats.json", json);
    }
}

Generated JSON Output


{
  "Bhutan": 3,
  "Australia": 2,
  "UK": 1,
  "India": 3,
  "Nepal": 1,
  "New-Zealand": 1,
  "Sri-Lanka": 1
}

Watch the Full Video Tutorial

You can watch the full step-by-step explanation here:

Watch the YouTube video


One-to-One ASP.NET / .NET Mentoring

I provide personal one-to-one training for developers who want to master:

  • C#
  • ASP.NET Core
  • Web API
  • Entity Framework
  • SQL Server
  • Blazor
  • .NET Interview Preparation

Website:
https://supernovaservices.com

WhatsApp for training inquiries:
+91-9331897923

Class Guidelines for Effective 1-on-1 Learning

To keep every session productive and distraction-free, please follow these simple guidelines:

  • Quiet Environment: Join from a calm, private room with minimal background noise. Avoid public or noisy places.
  • No Interruptions: Inform family/roommates in advance. Keep doors closed during class.
  • Mobile on Silent / DND: Set your phone to Silent or Do Not Disturb to prevent calls and notifications.
  • Be Fully Present: Do not multitask. Avoid attending to other calls, visitors, or errands during the session.
  • Stable Setup: Use a laptop/desktop with a stable internet connection and required software installed (Visual Studio/.NET, SQL Server, etc.).
  • Punctuality: Join on time so we can utilize the full session effectively.
  • Prepared Materials (If any): Keep project files, notes, and questions ready for quicker progress.

Following these guidelines helps you focus better and ensures I can deliver the best learning experience in every class.

Schedule a Quick 10-Minute Call

I prefer to start with a short 10-minute free call so I can understand:

  • Your learning objectives and career goals
  • Your current skill level
  • The exact topics you want to learn

Why? Because course content, teaching pace, and fees all depend on your needs — there’s no “one-size-fits-all” pricing. Please leave your details below, and I’ll get back to you to arrange a convenient time for the call.




Note: Payment is made only after your first class, once you’re completely satisfied. However, fees paid after the first class are non-refundable. This helps maintain scheduling commitments and allows me to reserve your preferred time slot with full attention.

Google Review Testimonials

.NET Online Training
Average Rating: 4.9
Votes: 50
Reviews: 50