37

Migrating from the legacy .NET Framework I need to create a long time background process worker.

Looking at the documentation I found a BackgroundService class, which is used for this kind of purpose. But I stumbled across two the same (for my point of view) methods ExecuteAsync() and StartAsync()

Can somebody explain to me what the main difference between them? Is it some kind of segregation principle - we have a method for setting up data as the "constructor" and we have a method for actually doing things?

Theodor Zoulias
  • 34,835
  • 7
  • 69
  • 104
osynavets
  • 1,199
  • 1
  • 12
  • 22

3 Answers3

60

The default behavior of the BackgroundService is that StartAsync calls ExecuteAsync, see code. It's a default, the StartAsync is virtual so you could override it.

Please note that only StartAsync is public and ExecuteAsync protected (and abstract). So from the outside StartAsync is called

If you create a subclass of BackgroundService, you must implement ExecuteAsync (because it's abstract). That should do your work. Also you could override StartAsync (as it's virtual), but that's only needed for special cases.

So why is there a StartAsync and ExecuteAsync?

You could create a service by implementing IHostedService. That interface has StartAsync and StopAsync.

BackgroundService is an (base) implementation of IHostedService, and could be used for long running tasks. This one defines the abstract ExecuteAsync.

In summary

  • When inheriting from BackgroundService, implement ExecuteAsync
  • When implementing IHostedService, implement StartAsync and StopAsync

Read more

Julian
  • 33,915
  • 22
  • 119
  • 174
  • 2
    You typically don't need to override `StartAsync`. It is there as a part of `IHostedService` implementation. All the work should be done in `ExecuteAsync`. – Alex Skalozub Feb 22 '20 at 20:31
  • Yes indeed! Updated my post to make it more clear. Thanks! – Julian Feb 22 '20 at 20:35
  • 1
    I would just be interested in your opinion - I think `BackgroundService` doesn't implement `IDisposable` properly as `CancellationTokenSource` is managed type so I would expect to see code like [this](https://stackoverflow.com/a/18337005/3242721). So if this class doesn't even implement `IDisposable` as it should, is there any point in using it rather than implementing `IHostedService`? – Michal Hosala Oct 20 '20 at 19:53
  • The point to consider is that even though `ExecuteAsync` is async method, asynchronous calls (with `await` keyword) will not be awaited. So, if we use part of `ExecuteAsync` as a kind of startup and we need its results during or before app initialization, we should use them synchronously before first awaited call. – Niksr Feb 04 '23 at 16:39
2

To add to Julian's answer, I have noticed if you do override StartAsync when using the BackgroundWorker, ExecuteAsync will not get called. I am not sure if you are supposed call ExecuteAsync from StartAsync, but my impression was StartAsync runs before ExecuteAsync and before the backgroundservice runs. But the behavior I am seeing is that if you override StartAsync, ExecuteAsync will not automatically get called. Remove the override to StartAsync, and ExecuteAsync gets called automatically.

Eric Aya
  • 69,473
  • 35
  • 181
  • 253
Reid K
  • 21
  • 3
  • I noticed the same thing and found that if I return `base.StartAsync(cancellationToken);` from my overridden `StartAsync` method, `ExecuteAsync` will still get called. Looking at the code via the link in Julian's answer, the virtual `StartAsync` method calls ExecuteAsync (`_executingTask = ExecuteAsync(_stoppingCts.Token);`), so I suppose you could do the same in your overridden StartAsync method (return a call to your overridden ExecuteAsync method from StartAsync). But I feel calling `base.StartAsync()` and letting the framework handle that is probably better? – Gregg L Dec 03 '22 at 14:54
  • @Reid K This is because `ExecuteAsync ` is called from original `StartAsync `. If you override it, then you have to repeat [original code](https://github.com/dotnet/runtime/blob/e3ffd343ad5bd3a999cb9515f59e6e7a777b2c34/src/libraries/Microsoft.Extensions.Hosting.Abstractions/src/BackgroundService.cs#L37) too. – Niksr Feb 04 '23 at 16:01
0

Expanding on Reid K's answer (edited by Eric Aya)...including the base.StartAsync() - which vs2022 gives you by default - will solve the problem.

        public override Task StartAsync(CancellationToken cancellationToken)
        {
            return base.StartAsync(cancellationToken);
        }