skip to Main Content

I have this simple minimal API:

var builder = WebApplication.CreateBuilder(args);

// my long running process <----
builder.Services.AddHostedService<MyBackgroundService>();    

var app = builder.Build();
app.MapGet("/", () => "Hello World!");
app.Run();

And here is MyBackgroundService:

public class MyBackgroundService : BackgroundService
{
    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        while(stoppingToken.IsCancellationRequested == false)
        {
            await Task.Delay(1000);             // wait 1 second
            Console.WriteLine("doing work..."); // do work
        }
    }
}

And here is the same program written using a Task instead of using a background service:

var builder = WebApplication.CreateBuilder(args);

// my long running process in a task instead of a background service
_ = Task.Run(async () =>
{
    while (true) {
        await Task.Delay(1000);             // wait 1 second
        Console.WriteLine("doing work..."); // do work
    }
});

var app = builder.Build();
app.MapGet("/", () => "Hello World!");
app.Run();

Why do tutorials and places on the internet encourage you to use BackgroundService? Will it be wrong practice if I use the second approach where I use Task.Run? What will be the difference of using the first approach vs using the second approach?

3

Answers


  1. It’s up to you.

    Basically, it’s a question of whether to use the existing framework or do it your own way.

    If it’s a mini project for up to 3 people and you’re not planning to deploy to Azure, I wouldn’t be afraid to use a your own solution – Task.
    Otherwise, I would reach for BackgroundService.

    Nicely described here: https://learn.microsoft.com/en-us/aspnet/core/fundamentals/host/hosted-services?view=aspnetcore-6.0&tabs=visual-studio

    • BackgroundService allows integration into a larger ecosystem with rich monitoring capabilities
    • Task is always available, straightforward and simple – but you solve everything yourself

    Tip: look for your piece of code

    _ = Task.Run(async () => ...
    

    are you sure that that discard _ is ok?

    Login or Signup to reply.
  2. After review the source code of the Host class, I am pretty sure that a BackgroundService is a normal task except for the following features:

    1. It gets notified when the application starts or stops.

    2. It handles exceptions like OperationCanceledException.

    Login or Signup to reply.
  3. This is a question about software architecture (even if you don’t realize it).

    Sure you can put everything in one big file, heck you don’t even need those things called "subroutines". I mean in the 60s they could work perfectly fine without them…. but of course programs were quite small back then.

    To keep software maintainable, testable, reliable, etc many software development techniques have combined into certain "architectures", "principles", "guidelines", etc. i.e. the way programs are build.

    Let’s start with "subroutines". Those weren’t always there. At one moment somebody discovered that repeating code could be put in a separate place and called over and over. And it tuned out that separating code out made it better readible, maintainable, testable etc.

    Another one of these is inversion of control, say dependency injection. This one is used for the background service, where you can resolve and inject other services during the construction of the bg service object.

    Another one is the singleton, i.e. there being one and only one instance of the worker. Of course you could make that yourself, but the framework offers you a standardized way to build such a singleton background worker. So it’s a kind of a "builder" which is also a design pattern. Which helps you with another principle: don’t repeat yourself. Don’t repeat yourself.

    Are you slowly starting to see the big picture?

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search