skip to Main Content

I’m using ASP.Net and I have dependency injection setup in a way that IRepository is added as scoped. Everything works fine in request response scenario.

But I also added BackgroudService which is executed once every minute and need to take some fresh data. In order to do that I can’t register IRepository as a singleton, I would like it to be created each time it is needed. What is the best option to do it in asp.net using built in IoC container?

2

Answers


  1. Inject IServiceProvider instead of the repository. When needed, manually create a scope by calling the CreateScope (with using to dispose the service scope when leaving scope) and then use GetRequiredService to get an instance of your IRepository implementation.

    This is all detailed with an example in Microsoft’s page on background tasks and hosted services in ASP.NET Core. Example from that page:

    public class ConsumeScopedServiceHostedService : BackgroundService
    {
        private readonly ILogger<ConsumeScopedServiceHostedService> _logger;
    
        public ConsumeScopedServiceHostedService(IServiceProvider services, 
            ILogger<ConsumeScopedServiceHostedService> logger)
        {
            Services = services;
            _logger = logger;
        }
    
        public IServiceProvider Services { get; }
    
        protected override async Task ExecuteAsync(CancellationToken stoppingToken)
        {
            _logger.LogInformation(
                "Consume Scoped Service Hosted Service running.");
    
            await DoWork(stoppingToken);
        }
    
        private async Task DoWork(CancellationToken stoppingToken)
        {
            _logger.LogInformation(
                "Consume Scoped Service Hosted Service is working.");
    
            using (var scope = Services.CreateScope())
            {
                var scopedProcessingService = 
                    scope.ServiceProvider
                        .GetRequiredService<IScopedProcessingService>();
    
                await scopedProcessingService.DoWork(stoppingToken);
            }
        }
    
        public override async Task StopAsync(CancellationToken stoppingToken)
        {
            _logger.LogInformation(
                "Consume Scoped Service Hosted Service is stopping.");
    
            await base.StopAsync(stoppingToken);
        }
    }
    
    Login or Signup to reply.
  2. I would recommend using existing solutions for scheduled background tasks, e.g. Hangfire.

    In scenarios like yours, I prefer to register a separate SchedulerService that controls tasks that must be started on a schedule or repeated every time interval, e.g., run a task each minute, as in your case.

    Here is an example from my project:

    public class MyBackgroundService(IServiceProvider serviceProvider) : BackgroundService
    {
        private bool _isFirstRun = true;
    
        protected override async Task ExecuteAsync()
        {
            using var scope = serviceProvider.CreateScope();
            
            //We need to register all the jobs just once
            if (_isFirstRun)
            {
                var worker = scope.ServiceProvider.GetRequiredService<ISchedulerWorker>();
                worker.Start();
    
                _isFirstIteration = false;
            }
        }
    }
    

    The simple implementation Scheduler worker where you can call your methods

    public class SchedulerWorker(IRepository repository) : ISchedulerWorker
    {
        public void Start()
        {
            //Register all the jobs you need to run
            RecurringJob.AddOrUpdate(nameof(TakeFreshDataAsync), () => TakeFreshDataAsync(), Cron.Minutely);
        }
    
        public Task TakeFreshDataAsync() => repository.TakeFreshDataAsync();
    }
    

    And here is the registration of all the services in Program.cs:

    
    services.AddScoped<ISchedulerWorker, SchedulerWorker>();
    services.AddScoped<IRepository, Repository>();
    services.AddHostedService<MyBackgroundService>();
    
    //You can use other providers; it is not recommended to use MemoryStoray for production
    services.AddHangfire(config => { config.UseMemoryStorage(); });
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search