Home > OS >  How to use the database from a BackgroundService in .NET Web API
How to use the database from a BackgroundService in .NET Web API

Time:05-24

I am building a microservice which is supposed to send periodical notifications through various means.

To process the notifications and triggers I intend to use a BackgroundService which will look into database, call appropriate service based on notification type and mark into the database the notification as being sent.

How can I access the database from the background service in a safe way, not having concurrency issues?

Is it enough to inject IServiceProvider and create a scope?

public class MyBackgroundService : BackgroundService
{
    private readonly IServiceProvider _serviceProvider;

    public MyBackgroundService(IServiceProvider serviceProvider)
    {
        _serviceProvider = serviceProvider;
    }

    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        await DoWorkAsync(stoppingToken);
    }

    private async Task DoWorkAsync(CancellationToken stoppingToken)
    {

        using (IServiceScope scope = _serviceProvider.CreateScope())
        {
            IRepository<Notification> notificationRepository =
                scope.ServiceProvider.GetRequiredService<IRepository<Notification>>();
            IRepository<NotificationLog> notificationLogRepository =
                scope.ServiceProvider.GetRequiredService<IRepository<NotificationLog>>();
            IUnitOfWork unitOfWork = 
                scope.ServiceProvider.GetRequiredService<IUnitOfWork>();

                
            while(true)
            {
                 if(stoppingToken.IsCancellationRequested)
                 {
                     return;
                 }

                var list = await notificationRepository.GetAll();
                .....................................
                await notificationLogRepository.Add(...);
                await unitOfWork.Commit();
            }
        }
    }

    public override async Task StopAsync(CancellationToken stoppingToken)
    {

        await base.StopAsync(stoppingToken);
    }
}

CodePudding user response:

Is it enough to inject IServiceProvider and create a scope?

Yes.

While you can create a single scope for your entire background worker's lifetime, you also can create a scope per "invocation" - in this case, you could create a new scope each time the timer goes off. That way, if there's ever a situation where the next one starts before the current one completes, the two invocations will be guaranteed to have different scopes.

If your "timer" is just doing an await Task.Delay, though, then there's no possibility of overlapping invocations and separate scopes aren't necessary. Some people prefer them anyway, since "invocation" and "scope" conceptually go well together.

  • Related