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

NOTE: This content is now quite out of date. The implementation described below is now available in .NET Core by deriving from the BackgroundService class. I cover this topic in detail in my new course on Pluralsight – “Building ASP.NET Core Hosted Services and .NET Core Worker Services“.

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.


Have you enjoyed this post and found it useful? If so, please consider supporting me:

Buy me a coffeeBuy me a coffee Donate with PayPal

Steve Gordon

Steve Gordon is a Pluralsight author, 6x Microsoft MVP, and a .NET engineer at Elastic where he maintains the .NET APM agent and related libraries. Steve is passionate about community and all things .NET related, having worked with ASP.NET for over 21 years. Steve enjoys sharing his knowledge through his blog, in videos and by presenting talks at user groups and conferences. Steve is excited to participate in the active .NET community and founded .NET South East, a .NET Meetup group based in Brighton. He enjoys contributing to and maintaining OSS projects. You can find Steve on most social media platforms as @stevejgordon

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

    1. 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.

      1. 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.

  1. 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.

  2. 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

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

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

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

  5. 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… 🙂

  6. 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.

  7. 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

    1. 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.

  8. Glad your deliberations resulted in you posting this.

    I have a question about line 17 of HostedService… Why have the condition? If it is completed it’s just replacing one completed Task with another. Why isn’t it just “return _executingTask”?
    Something to do with the lifetime of the Task object?

  9. Seems this is now called Microsoft.AspNetCore.Hosting.Internal.HostedServiceExecutor ? Impossible to keep up!

    1. Hi Simon. That’s the internal class which will trigger the execution of any services that implement IHostedService which you’ve registered. You shouldn’t need to work with directly.

  10. Hi Azret,

    I’m glad you found it useful. Thanks for the feedback.

    I just saw this today and happened to be able to ask the men in the know (David Fowler and Damian Edwards) at NDC. The answer is that they are executed in the order registered. So if you register MyService2 first, it will be executed first before MyService1.

    Depending on your scenario it might also make sense for you to have a dependency on MyService1 from MyService2 directly and controlling the activity that way?

    Steve

  11. I modified the HostedService base class to utilize Cron Time Strings (using nuget pkg NCrontab) and making it take care of the scheduling. This removes any looping concern classes inheriting from HostedService to only declare the function they would like to execute (Task ExecuteAsync) as well as the schedule (string Schedule)

    I added this function in HostedService

    private async Task ScheduledTask(CancellationToken ct)
    {
    var schedule = CrontabSchedule.Parse(Schedule);

    do
    {
    await Task.Delay(schedule.GetNextOccurrence(DateTime.Now) – DateTime.Now);

    await ExecuteTask(ct);
    }
    while (!ct.IsCancellationRequested);
    }

    ///
    /// Follows Cron String Formatting
    /// [sec(optional) min hour day-of-month month day-of-week]
    ///
    public abstract string Schedule { get; }

    and changed the private Task assignation to:
    _executingTask = ScheduledTask(_cts.Token);

  12. Hi Lev,

    Thanks for the feedback. The one shown in the MSDN blog is actually included in the upcoming 2.1 release and would represent the “best” approach. The team are working on documenting various background task use cases.

    The tasks are pretty much expected to run for the lifetime of the app. You might be able to do something with linked cancellation tokens but not sure exactly what would work there. Another option is perhaps depending on a shared configuration class which the controller can update to disable the task. The background service could then check that and either no-op or possible cancel that way.

    1. Interesting. Your implementation actually looks more robust giving chained tokens.

      I was able to successfully use an injected IApplicationLifetime.StopApplication inside of an async controller get method. It worked nicely.

Leave a Reply

Your email address will not be published. Required fields are marked *