If you’re working with ASP.Net Core chances are that sooner or later you’ll find yourself in a scenario where you would ask yourself “how could I run a background task in my ASP.Net Core app?” It doesn’t really matter if you’re working on an API or a powerful MVC application. It’s very often the case that your application will need to check for some updates in regular intervals, do some continuous processing outside of the MVC flow or simply perform some work when the web server fires up. Fortunately, ASP.Net Core is well prepared for these scenarios giving developers the option to run background tasks as hosted services.
Since I worked with hosted services in several occasions my goal with this article is to offer an overview on hosted services in ASP.Net Core and outline some aspects that you would probably need to take into consideration. If what you want is simply a “getting started”-style of article, the official Microsoft docs are better suited and you may want to return to this article later. So let’s get started!
A hosted service is a class with background task logic that implements the IHostedService interface. By implementing this interface you can create background tasks that run on a specific time interval, queued tasks that run sequentially or some continuous processing tasks. Implementing the interface is not very complicated since there are two methods that need to be implemented: StartAsync() and StopAsync(). As the name suggests, both are async methods so they simply need to return a Task. Here’s a very simple implementation of the StartAsync() method:
public Task StartAsync(CancellationToken cancellationToken)
_timer = new Timer(SyncEmployees, null, TimeSpan.Zero,
And for the StopAsync() method:
public Task StopAsync(CancellationToken cancellationToken)
Looking at the StartAsync() method it seems obvious that we’re creating a background task that will run in a regular time interval. Notice that when we’re creating the timer we simply specify the callback method as the first input parameter in the constructor. This means that the callback method will be executed each time the timer reaches the defined point (each 60 seconds in this case). All the logic for the hosted services goes there.
What about the cancellation token?
At this point many people would ask what’s that cancellation token that we have as input parameter both on the StartAsync() and StopAsync() methods all about? Generally cancellation tokens are used to indicate that a certain process is shutting down. This might be useful to know for services or classes in general that might consume information made available by the process that shuts down. It’s useful because we can implement whatever logic we want when we know that a certain process shuts down.
When talking about hosted services in ASP.Net Core cancellation tokens are used for the same purpose. To be more specific, the cancellation token simply informs us that cancellation has been requested. Or, in other words, that the host is shutting down. If this is the case, we can implement logic in our hosted service to gracefully close connections or simply stop processing.
The cancellation token has a default five second timeout to indicate that the shutdown process should no longer be graceful. However, tasks aren’t abandoned after cancellation is requested. The caller awaits all tasks to complete. If the app shuts down unexpectedly (for example, the app’s process fails), StopAsync() might not be called. Therefore, any methods called or operations conducted in StopAsync() might not occur. This is something developers need to be aware of!
Can our live be even easier?
Yes, it can. Most of the hosted services that you’ll need to create have really similar cancellation needs. Therefore, meet the BackgroundService class in Microsoft.Extensions.Hosting. This class provides all the main work needed to set up the background task. It implements a standard cancellation token management logic and exposes an abstract method called ExecuteAsync() that you would need to implement in your own class.
So, if you want to run a calculator hosted service you would simply need to create a Calculator class that inherits from the BackgroundService class and implement the ExecutAsync() method in your class. And that’s really it!
Registering hosted services
It goes without saying that hosted services need to be registered in ConfigureServices. Otherwise your hosted services won’t do anything. Fortunately, in ASP.Net Core this is really trivial. The ServiceCollection in your Startup.cs file exposes an extension method called AddHostedService(). The method is generic so you can specify the implemeting class. It would look similar to this:
After this step your hosted services will be started when the server starts. However, please notice that depending on the host that you are using your hosted service might start at a slightly different point. Still you are always guaranteed that your service will be called into action during the startup process (of course, if no exceptions occur).
Using scoped services in a hosted service
In virtually all scenarios you will need to use services from the DI container. Transient and singleton services are really easy to use. You simply need to inject them in your hosted service class. Scoped services are, however, trickier. Why? Well, because scoped services are created on a per request basis. This means that as a request comes in to your application, ASP.Net core basically creates a scope for that specific request and resolves all scoped services. This is why you won’t have any problems to inject scoped services in your controllers.
To overcome this difficulty you would need to create a scope in your hosted service. This is not difficult at all since you can inject the IServiceProvider in your hosted service. And you can use it to create a scope and resolve the necessary scoped services. Here’s a sample that illustrates the concept:
private async void SyncEmployees(object state)
using (IServiceScope scope = Services.CreateScope())
_db = scope.ServiceProvider.GetRequiredService<IEmployeeDbProcessor>();
_client = scope.ServiceProvider.GetRequiredService<IEmployeeApiClient>();
_rabbit = scope.ServiceProvider.GetRequiredService<IMessagePublisher>();
bool syncSuccess = await StartSynchronization();
Are hosted services running on a different thread?
I have to admit that the official Microsoft documentation is a little bit misleading because it talks about “background tasks”. And usually when we use this term we tend to think that it refers to a different thread. So we might assume that all the MVC stuff is running on one thread and the background work (so the hosted service) is running on a different one. However this is not the case with hosted services in ASP.Net Core!
The first thing to realize is that there is no true background work in the context of a web application. Web servers are designed to quickly service requests. Sure there is a thread pool but usually it is used to service requests. That’s also why you usually don’t manually create threads in web application as you often do when developing desktop applications. The moment you take a thread out from the pool there is one thread less to service your incoming requests and that’s not wise in my opinion.
Long story short: hosted services don’t run on separate threads. Also don’t create threads manually in a web application. If there’s any work that needs to be done that takes a not insignificant amount of time, that work should be offloaded to something running outside the context of your web application that can do the work without affecting the performance of your web application.
Hosted services in ASP.Net Core are really great and I use them a lot. However, there are circumstances in which it would be wise to ask ourselves if we really need a hosted service? As mentioned in the previous discussion point, this could make your application less performant. So what other options would we have?
Being an Azure guy I really can’t help my self to note that most of the hosted services you would want to create can also be executed using Azure Functions. Going serverless is always a good thing in my opinion if the circumstances permit it. The main advantage is that your application’s performance won’t be affected by the background work you need to do. On the other hand, it creates a technology debt so if you want you application to be easily movable between different platform or cloud providers having a big technical debt won’t help you at all.
Hosted services and microservices
For a couple of months I am working on a microservices project. The cool think is that we started it from scratch. So from a microservices point of view you’ll soon notice that you would need hosted services in a lot of different places. So initially we just started to create them where we needed. We soon realized that this approach might not be very helpful because as the project grows you loose the overview on what services are used and where.
The solution here would be to create a separate microservice or container only for the hosted services. Each hosted service would put the relevant information on the message bus and other services would consume that information as needed. By doing this you always know that all your hosted services are in the same place and it’s easier to maintain them.
That’s it! The blog post is surely longer than I would have expected but I think it contains all relevant information for those who are new to hosted services in ASP.Net Core. If you have other things to add, feel free to drop a comment. Cheers!
How useful was this post?
Click on a star to rate it!
Average rating / 5. Vote count: