Implement background tasks using IHostedService and access scoped service using IServiceScopeFactory
Implement background tasks using IHostedService and access scoped service using IServiceScopeFactory

It is easy to implement Background tasks and scheduled work using IHostedService and the BackgroundService class.

Sometimes it is required certain work to be offloaded when a Web API request is processed. That work could be accessing database records and update some information on some tables based on existing data.

In this post, I'm going to show how to Implement background tasks using IHostedService and the BackgroundService class that is suggested by the MSDN article.

Also, I will cover how to access your scoped service from this background task. This way you can access the service to talk to your database to process some information.

From NET Core 2.1 and up, you can use named IHostedService that helps you to easily implement hosted services.

We will register the hosted service that runs in the background while your web host or host is running.

You have to register the hosted services within the familiar ConfigureServices() method of the Startup class, as in the following code from a typical ASP.NET WebHost.

public static void Main(string[] args)
    {
        CreateHostBuilder(args).Build().Run();
    }

    public static IHostBuilder CreateHostBuilder(string[] args) =>
        Host.CreateDefaultBuilder(args)
            .ConfigureWebHostDefaults(webBuilder =>
            {
                webBuilder.UseStartup();
            })
            .ConfigureServices(services =>
            {
                services.AddHostedService<StatusCheckerService>();
            });

Use only one way to register from these two options.

public IServiceProvider ConfigureServices(IServiceCollection services)
{
    //Other DI registrations;

    // Register Hosted Services
    services.AddHostedService<StatusCheckerService>();
    services.AddHostedService<MyHostedServiceA>();
    services.AddHostedService<MyHostedServiceB>();
    //...
}

Next is to use the Background base class (available only from dot net core 2.1)

/// 
/// Base class for implementing a long running .
/// 
public abstract class BackgroundService : IHostedService, IDisposable
{
    private Task _executingTask;
    private readonly CancellationTokenSource _stoppingCts =
       new CancellationTokenSource();

    protected abstract Task ExecuteAsync(CancellationToken stoppingToken);

    public virtual Task StartAsync(CancellationToken cancellationToken)
    {
        // Store the task we're executing
        _executingTask = ExecuteAsync(_stoppingCts.Token);

        // If the task is completed then return it,
        // this will bubble cancellation and failure to the caller
        if (_executingTask.IsCompleted)
        {
            return _executingTask;
        }

        // Otherwise it's running
        return Task.CompletedTask;
    }

    public virtual async Task StopAsync(CancellationToken cancellationToken)
    {
        // Stop called without start
        if (_executingTask == null)
        {
            return;
        }

        try
        {
            // Signal cancellation to the executing method
            _stoppingCts.Cancel();
        }
        finally
        {
            // Wait until the task completes or the stop token triggers
            await Task.WhenAny(_executingTask, Task.Delay(Timeout.Infinite,
                                                          cancellationToken));
        }

    }

    public virtual void Dispose()
    {
        _stoppingCts.Cancel();
    }
}

When deriving from the above abstract base class, you just need to implement the ExecuteAsync() method in your own custom hosted service class as shown below.

using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using System;
using System.Linq;
using System.Threading;
using System.Threading.Tasks; 

namespace Services // your namespace where this class is placed
{
    public class StatusCheckerService: BackgroundService
    {
        private readonly ILogger<StatusCheckerService> logger; 

        public StatusCheckerService(
            ILogger<StatusCheckerService> logger,
        {          
            this.logger = logger;
        }
        protected override async Task ExecuteAsync(CancellationToken stoppingToken)
        {
            logger.LogDebug($"StatusCheckerService is starting."); 

            stoppingToken.Register(() =>
                logger.LogDebug($"StatusCheckerService background task is stopping."));
            while (!stoppingToken.IsCancellationRequested)
            {
                
	// MyMethodOrLogic goes here. You may call any method inside this class.
 logger.LogDebug($"StatusCheckerService task doing background work.");
 
                await Task.Delay(5000, stoppingToken); // Time to pause this background logic
            } 
            logger.LogDebug($"StatusCheckerService background task is stopping.");

        }
    }
} 

For more detailed information on this topic, refer to this post from the MSDN article. My intention in this post is to show you how to access scoped service in this background task and communicate with the database.

Now, you wonder how to access Scoped Service from this background task?

Using IServiceScopeFactory Interface. A factory for creating instances of IServiceScope, which is used to create services within a scope

Complete logic to access scoped service.

 public class StatusCheckerService: BackgroundService
    {
        private readonly ILogger<StatusCheckerService> logger; 
		private readonly IServiceScopeFactory serviceScopeFactory;

        public StatusCheckerService(

            ILogger<StatusCheckerService> logger,IServiceScopeFactory serviceScopeFactory)
        {          
            this.logger = logger;
            this.serviceScopeFactory = serviceScopeFactory;
        }
 

        protected override async Task ExecuteAsync(CancellationToken stoppingToken)

        {

            logger.LogDebug($"StatusCheckerService is starting."); 

            stoppingToken.Register(() =>
                logger.LogDebug($"StatusCheckerService background task is stopping."));
            while (!stoppingToken.IsCancellationRequested)
            {
                
		using (var scope = serviceScopeFactory.CreateScope())
                {
	// You can ask for any service here and DI will resolve it and give you back service instance
var contextService = scope.ServiceProvider.GetRequiredService<IContextService&ht;(); 
				   contextService.YourMethodOrServiceMethod(); //access dbcontext or anything thats available form your service
				}
                logger.LogDebug($"StatusCheckerService task doing background work."); 

                await Task.Delay(5000, stoppingToken); // Time to pause this background logic
            } 
            logger.LogDebug($"StatusCheckerService background task is stopping.");
        }
    }

Related Posts

Conclusion

In this post, I showed how to Implement background tasks using IHostedService and the BackgroundService class and access scoped service. That’s all from this post. If you have any questions or just want to chat with me, feel free to leave a comment below

6 thoughts on “Implement background tasks using IHostedService and access scoped service using IServiceScopeFactory

  1. I was very pleased to discover this site. I want to to thank you for your
    time just for this wonderful read!! I definitely appreciated every part of it and I
    have you saved to fav to look at new things on your website.

  2. Good day I am so thrilled I found your blog page, I really found you by mistake, while I was browsing on Aol for something else, Regardless I am here now and would
    just like to say many thanks for a fantastic post and a all
    round enjoyable blog (I also love the theme/design), I don’t have time
    to read it all at the moment but I have saved it and also added your RSS feeds, so when I have time
    I will be back to read a great deal more, Please do keep up the superb jo.

  3. I constantly emailed this webpage post page to all
    my contacts, because if like to read it afterward
    my friends will too.

Leave a Reply

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

Verified by MonsterInsights