Implementing IHostedService in ASP.NET Core 2.0 Use IHostedService to run background tasks in ASP.NET Core apps

Update 30-08-2017: ASP.NET Core 2.0.0 is now released. I have updated my sample repo to 2.0.0.

I’ve had chance to play around with ASP.NET Core 2.0 preview 2 a little in the last few weeks. One of the things I was keen to try out and to understand a little better was the new IHostedService interface provided by Microsoft.Extensions.Hosting. David Fowler and Damian Edwards demonstrated an early example of how to implement this interface using the preview 1 of ASP.NET Core 2.0 at NDC Oslo. At the time of that demo the methods were synchronous but since then they have been made asynchronous.

Full disclosure: After taking a first pass at creating something using this interface I ran the code past David Fowler and he kindly reviewed it. As I suspected, I was not using it correctly! Since then David was kind enough to answer a few questions and even provided a sample of a base class that simplifies creation of hosted services. After my failure to understand the expected implementation of the interface and a general realisation that I needed to learn more about using Task cancellations with async/await, I almost decided to ditch my plan to write this blog post. However, I realised that this is still probably a good sample to share since others may run into the same mistakes I did. After speaking with David I believe this is appropriate use of the interface.

One of the challenges when starting out was trying to use something in preview that had no samples or documentation yet. While I hope no one will use this prior to RTM of 2.0 when I expect full documentation will be made available, I’m sure people may benefit from taking a look at it sooner. David did say that they have intentions to provide a formal base class, much like the code that he provided for me which will make creating these background hosted services easier. However, that won’t make it into the 2.0 release. The code I include in this sample might act as a good starting point until then, although it’s not fully tested.

Remember; there are no docs for this interface currently, so I’m taking a best guess at how it can be used and how it’s working based on what I’ve explored and been able to learn from David. This feature is preview and may also change before release (although very unlikely as 2.0 is nearly baked now). If you’re reading this in the future (and unless you’re a time traveller, you must be) please keep in mind this may be outdated.

Hosted Services

The first question to answer is what can we use this interface for? The basic idea is that it allows us to register background tasks, that run while our web host is running. These are co-ordinated with the lifetime of the application. We register a Task when the application starts and have the opportunity to do some graceful clean-up when the application is shutting down. While we could spin off work on a background thread previously, it would be killed when the main application process shutdown.

To create these background tasks we implement the new IHostedService interface.

The interface looks like this:

The idea is we register one or more implementations of this interface with the DI container, all of which will then be started and stopped along with the application by the HostedServiceExecutor. As users of this interface we are responsible for properly handling the cancellation and shutdown of our services when StopAsync is triggered by the host.

Creating a Hosted Service

One of the first possible use cases for these background tasks that I came up with was a scenario where we might want to update some content or data in our application from an external source, refreshing it periodically. Rather than doing that update in the main request thread, we can offload it to a background task.

In this simple example I provide an API endpoint which returns a random string value provided from an external service and updated every 5 seconds.

The first part of the code is a provider class which will hold the string value and which includes an update method that when called, will get a new string from the external service.

I then use this provider from my controller to return the string.

The main work for the setup of our service lives in an abstract base class called HostedService. This is the base class that David Fowler kindly put together. The code looks like this:

The comments in the class describe the flow. When using this base class we simply need to implement the ExecuteAsync method.

When the StartAsync method is called by the HostedServiceExecutor a CancellationTokenSource is created and linked to the token which was passed into the StartAsync method. This CancellationTokenSource is stored in a private field.

ExecuteAsync, an abstract method is then called, passing in the token from our CancellationTokenSource and the returned Task itself is stored. The StartAsync then must return as complete to the caller. A check is made in case our ExecuteAsync method is already completed. If not we return a Task.CompletedTask.

At this point we have a background task running whatever code we placed inside our implementation of ExecuteAsync. Our WebHost will go about its business of serving requests.

The other method defined by IHostedService is StopAsync. This method is called when the WebHost is shutting down. This is the key differentiator from running work on a traditional background thread. Since we have a proper hook into the shutdown of the host we can handle the proper shutdown of our workload on the background thread.

In the base class, first a check is made to ensure that StartAsync was previously called and that we actually have an executing task. Then we signal cancellation on our CancellationTokenSource. We await the completion of one of two things. The preferred option is that our Task which should be adhering to the cancellation token we passed to it, completes. The fall back is the Task.Delay(-1, cancellationToken) task completes. This takes in the cancellation token passed by the HostedServiceExecutor, which in turn is provided by the StopAsync method of the WebHost. By default this will by a token set with a 5 second timeout, although this timeout value can be configured when building our WebHost using the UseShutdownTimeout extension on the IWebHostBuilder. This means that our service is expected to cancel within 5 seconds otherwise it will be more abruptly killed.

To use this base class I then created a class inheriting from it called DataRefreshService. It is within this class that I implement the ExecuteAsync abstract method from HostedService. The ExcecuteAsync method accepts a cancellation token and looks like this:

I call the UpdateString method on the RandomStringProvider and then wait for 5 seconds before repeating. This all happens inside a while loop which continues indefinitely, until cancelation has been requested for the cancellation token. We pass the cancellation token down into the other async methods as well, so that they too can cancel their tasks all the way down the chain.

The final part of this is wiring up the dependency injection. Within the configure services method of Startup I must register the hosted service. I also register my RandomStringProvider class too, since that is passed into things via DI.

Summary

The IHostedService interface provides a nice way to properly start background work in a web application. It’s a feature we should not overuse, as I doubt it’s intended for spinning up large numbers of tasks, but for some scenarios it offers a nice solution. Its main benefit is the chance to perform proper cancellation and shutdown of our background tasks when the host itself is shutting down.

A special thanks to the amazing David Fowler for a quick code review (and correction) of my DataRefreshService. It was very kind of him to spare me some of his time to help me better understand this new feature. I hope I’ve explained everything correctly so that others can benefit from what he shared with me.

If you would like to view the source code from this post you can find it on my GitHub account.

  • Good work! I love this kind of “early explore” posts.

    • Agreed! The question is, is it the right tool for the job and should you use it? I’ve not tried Azure Functions yet but that seems a better fit for a scheduled backround task and it’s probably cheaper to run.

      • Steve Gordon

        Yes, I don’t think the intended use case is unrelated background tasks. If the work can be split from the web application it probably should be a separate service/function etc. My take on this is that it’s for simple cases where your web application needs to do something as part of it’s work but ideally not in the request flow. So my example is a basic one, but updating some periodically changing content that the web application relies on. Certainly you could achieve that in other ways as well. Another example might be flushing or repopulating in-memory caches for the application.

  • duncansmart

    Title should be “Implementing IHostedService in ASP.NET *Core* 2.0”

    • Steve Gordon

      Yep, good catch! Updated – Thanks.

  • Sherry

    I guess the main reason for introducing this abstraction is so that we develop modules in a clean and Interface driven manner. I very much like this approach. Thanks for sharing.

  • Steve Gordon

    Hi,
    You can register multiple implementations of IHostedService with the DI container. The HostedServiceExecutor will loop over the registered implementations and call each in turn.
    Steve

  • Steve Gordon

    Hi Yves,
    I’m not sure exactly on how the threading is handled in this case.
    Steve

  • Steve Gordon

    Hi Arish,
    I’ve not seen/used that interface before. Looks like it does provide similar functionality though.
    Steve

  • Josh

    When does the DataRefreshService get called?
    I can see in the controller that RandomStringProvider is called.
    I don’t get how this can work.

    • Steve Gordon

      As it implements IHostedService it is wired up by the framework. The HostedServiceExecutor (part of the framework) calls start and stop on all IHostedServices.

  • k3davis

    It appears that the sample base class provided by David Fowler no longer represents the actual interface released with asp.net core 2.0 rtm. Perhaps this post could be updated? Especially since this so far seems to be the only detailed documentation on this function… 🙂

  • Steve Gordon

    Could you provide more detail on what you’re trying to do?

  • David Fowler

    Did you add it to the DI container as an IHostedService?

  • David Fowler

    Threads are wasteful and tasks are much more efficient here. As long as you’re truly async you’ll be using the thread pool (or whatever scheduler you choose to use) extremely efficiently. Threads only exists as long as you’re not awaiting.

  • David Fowler

    Yes it’s similar and less DI friendly

  • Rene

    Hi Steve.
    Thank you for the great article (and David for the brilliant abstract class). It’s very enlightening and I made an implementation just like your example, but I have one problem. I deployed my projects to Azure as an App service. What happens is that webserver shuts down after a certain period of time. That means my hostedservice is also stopped. So, my question is obvious:
    How can I prevent the hostedservice from stopping?
    Cheers, Rene

    • Steve Gordon

      The hosted service is only expected to live as long as the web application. It’s advantage is that you have a mechanism to gracefully shutdown the backend service before the app closes. I know Azure shuts down apps after a period of inactivity so you might want to investigate the app setting “Always On” to ensure it’s kept alive. That will prevent the app and the service stopping. I guess you could also add a hosted service that calls a URL on your web app to keep it alive that way too.

      • Rene

        Ok. I’ll have a look at the Always on setting then. Thanks!