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

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.

Getting started with ASP.NET Core 2.0 Preview 1 A quick tour of the main structural changes

At Microsoft Build 2017 yesterday, the formal announcement of ASP.NET Core 2.0 and .NET Core 2.0 was made.

ASP.NET Core 2.0 preview announcement

.NET Core 2.0 preview announcement

For me it was no surprise that we were close with these announcements as I keep an eye on the ASP.NET Core repositories on GitHub where there have been preview 1 branches for a little while. Additionally, the team have shared some information on the weekly ASP.NET Community Standup shows that have helped us understand some of the changes coming our way.

In this post I want to focus in on a few changes to the basic structure of ASP.NET Core 2.0 applications which simplify the code needed to get started. Before looking at those elements, I felt it would be worth sharing the steps I took to get started with ASP.NET Core 2.0 Preview 1 on my development machine.

Getting started with ASP.NET Core 2.0 preview 1

The first step was to get the preview SDK for .NET Core 2.0 which can be safely installed alongside any prior 1.x SDK versions. The announcement post provides a link to download the SDK.

The next step was to install the SDK. Nice and easy!

Installing .NET Core 2.0 SDK

Initially I tried creating a project in the existing Visual Studio 2017 IDE. I did so by starting with an ASP.NET Core 1.1 project and simply updating it to target netcoreapp2.0 and the latest ASP.NET Core 2.0 packages. However, this was problematic since I couldn’t restore or build the solution inside Visual Studio. I did manage to do both of those tasks at the command line level using dotnet restore and dotnet build. Finally, running dotnet run I was up and running with a small sample API site. 

After querying the lack of Visual Studio support on Twitter, Damian Edwards confirmed that I would need to use the preview version of Visual Studio 2017 in order to work with ASP.NET 2.0 preview projects. So I went off to download and then install it. According to their website this is safe to install side-by-side with other versions. If you want to follow along, download Visual Studio 2017 preview from here.

Installing Visual Studio 2017 preview

Now that I had VS 2017 preview installed on my machine I decided to start fresh and try creating a new project directly inside the IDE. The new project dialog for an ASP.NET Core web project has been updated so I could simply choose to target that from the UI.

ASP.NET Core 2.0 project template

After a few moments I was up and running, working on a new ASP.NET 2.0 web application. A very painless experience so well done and thanks to the team for the work they’ve done to get us started on this preview release.

A Quick Tour of ASP.NET Core 2.0 preview 1

Now that I have a default ASP.NET Core 2.0 project on my machine, I want to highlight a few of the initial changes that you’ll see.

Simplified CSPROJ

After the move back to csproj that came with Visual Studio 2017, the new structure for these project files has been a great improvement on the past. Gone are the ugly GUIDs and the requirement to specify all files. With this release, the team have simplified things a step further.

In the mainstream Visual Studio 2017 release, targeting ASP.NET Core 1.1 a default csproj file looks like this:

In the latest version, we now have a single meta-package we reference to bring in all of the ASP.NET Core packages. This reduces a default csproj to just these few lines:

At first this concerned me a little. A key message behind the original ASP.NET Core release, was a “pay to play” model. You could specify only the packages you actually needed in your application, no longer bringing a dependency on a whole monolith framework. You could bring each version of each component that you actually used. This also resulted in a smaller footprint for your published application. My first impression was that we were taking a bit of a step back by instead referencing a single master package.

However after reading the announcement and chatting on Twitter, the consensus seems to be that this is not a problem. The SDK ships with the packages included with it, so you are never pulling these from NuGet directly. That had been a concern for me since at work we build our projects inside Docker containers. If each new build needed to pull all of the packages, even those we didn’t need, across the Internet, it could slow down our builds.

However, since they are local with the SDK, this is not a problem. Then I had a concern about a large deployment when you publish your application, including binaries that my application doesn’t even use. This also seems to be solved, as Microsoft now have a package trimming feature that they explain will exclude the binaries you are not using for your publish output. What remains to be seen is how the base packages can version truly independently since the meta package will define specific versions it depends on. When you depend on a version of AspNetCore.All you depend on the versions it defines. It seems that this might now require a new SDK release each time the underlying packages change so that you can work with a new version of the meta-package, while still having the packages locally.

Generally though, this new simplified file should make it very easy for people working outside of VS 2017 to get up and running with a hand coded project file if they so wish.

Another change you’ll notice is that the TargetFramework is now netcoreapp2.0 which defines that this project will run on .NET Core 2.0.

Simplified Program.cs

Next up, program CS got some refinement. To compare, in an ASP.NET Core 1.1 application, the class looks like this:

Now in 2.0 it looks like this:

This revised version utilises a C#6 syntax to build an expression body method called BuildWebHost. This method uses a new static method WebHost.CreateDefaultBuilder. This method provides a shortcut to get an IWebHostBuilder. Behind the scenes, this method will do a few things for us, saving a few lines of code. As per the source documentation, it will:

  1. Use Kestrel as the web server
  2. Set the ContentRootPath
  3. Load configuration from the default locations such as appsettings.json, user secrets and the environment variables. Note that configuration used to be defined inside the constructor of the Startup.cs file
  4. Configure the ILoggerFactory to log to console and debug. This also used to be inside Startup.cs, often defined in the Configure method
  5. Enables IISIntegration
  6. Add the developer exception page when in the development environment

If you want to look at the full source for the WebHost class, which lives inside the MetaPackages repository, you can take a look at the code up on GitHub.

Simplified Startup.cs

Startup.cs has also been simplified, mostly as a result of moving the logging and configuration code into the WebHost builder.

Previously a default ASP.NET Core 1.1 project would look like this:

Now it looks like this:

The constructor no longer takes an injected IHostingEnvironment, instead taking the IConfiguration dependency. With configuration sources already defined the constructor just sets the Configuration properly so we can access it when needed.

ConfigureServices is unchanged, but Configure is slightly shorter since it no longer has to define the logging.

Summary

There you have it. A quick tour of the basic structural changes you can expect when you start working with ASP.NET Core 2.0. I’m pretty happy with the changes. My initial concerns around the AspNetCore.All package are hopefully unfounded and the shifting of logging and configuration into extensions on IWebHostBuilder makes sense. The startup file is nice and clean by default and pretty much all code we add will be specifically to include services we require and to setup our pipeline.