Search code examples
c#asp.net-corebackground-service

How can I create a BackGround service that runs a function every given period of time ? Using C# (asp.net core 3.1.1)


I'm trying to make call to a function every specified interval of time, for that m using Background service, here is what I have done: Here is the Alerting controller where I have the function:

public class AlertingController : ControllerBase
{
    private readonly DatabaseContext _context;
    private readonly IMapper _mapper;

    public AlertingController(DatabaseContext context, IMapper mapper)
    {
        _context = context ?? throw new ArgumentNullException(nameof(context));
        _mapper = mapper ?? throw new ArgumentNullException(nameof(mapper));
    }
    public AlertingController()
    {

    }
//function that adds in the DB
    public async Task<AlertingResponse> GetAlertingToDB()
    {
        AlertingResponse dataGet;

        using (var httpClient = new HttpClient())
        {
            using (var response = await httpClient
                .GetAsync(MetricApiLink))
            {
                string apiResponse = await response.Content.ReadAsStringAsync();
                dataGet = JsonConvert.DeserializeObject<AlertingResponse>(apiResponse);
            }
        }
        if (dataGet.data.alerts != null || dataGet.data.alerts.Count > 0)
        {
            foreach (var alert in dataGet.data.alerts)
            {
                CreateAlertQuery QueryAlert = new CreateAlertQuery();
                QueryAlert.Name = alert.labels.alertname;
                QueryAlert.Instance = alert.labels.instance;
                QueryAlert.Serverity = alert.labels.severity;
                QueryAlert.Summary = alert.annotations.summary;
                QueryAlert.State = alert.state;
                QueryAlert.ActiveAt = alert.activeAt;
                var _Alert = _mapper.Map<AlertingDataModel>(QueryAlert);
                _context.Alertings.Add(_Alert);
                await _context.SaveChangesAsync();
            }
        }

        return null;
    }
}

I have tested the method with a HTTPGET request, it works fine, add the alerts into my database:

enter image description here

I have created a scooped service where I called the function GetAlertingToDB():

    internal interface IScopedAlertingService
    {
        Task DoWork(CancellationToken stoppingToken);
    }
    public class ScopedAlertingService : IScopedAlertingService
    {
        private int executionCount = 0;
        private readonly ILogger _logger;

    public ScopedAlertingService(ILogger<ScopedAlertingService> logger)
    {
        _logger = logger;
    }

    public async Task DoWork(CancellationToken stoppingToken)
    {
        AlertingController _AlertingToDB = new AlertingController();
        while (!stoppingToken.IsCancellationRequested)
        {
            executionCount++;

            _logger.LogInformation(
                "Scoped Processing Service is working. Count: {Count}", executionCount);

            await _AlertingToDB.GetAlertingToDB();

            await Task.Delay(10000, stoppingToken);
        }
    }
}

I have also created the Class that will consume my service, and will run in the BackGround:

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<IScopedAlertingService>();

            await scopedProcessingService.DoWork(stoppingToken);
        }
    }

    public override async Task StopAsync(CancellationToken stoppingToken)
    {
        _logger.LogInformation(
            "Consume Scoped Service Hosted Service is stopping.");

        await Task.CompletedTask;
    }
}

I injected the dependencies on the Startup Class and added the hosted service:

        services.AddHostedService<ConsumeScopedServiceHostedService>();
        services.AddScoped<IScopedAlertingService, ScopedAlertingService>();

The functions are working just fine untill a call the GetAlertingToDB() function and it doesn't work.

Any help would be great, thanks everyone :)


Solution

  • Personally I would rearrange your solution so that your background service doesn't need to create a Controller. Instead the controller, if you still need it at all, should call into your ScopedAlertingService where the work is performed once. Your background service can simply loop forever, with an await Task.Delay().

        public class ScopedAlertingService : IScopedAlertingService
        {
            public async Task DoWork(CancellationToken stoppingToken)
            {
                // move contents of your AlertingController.GetAlertingToDB here
            }
        }
    
        public class ConsumeScopedServiceHostedService : BackgroundService
        {
            private readonly IServiceProvider _services;
            public ConsumeScopedServiceHostedService(IServiceProvider services)
            {
                _services = services;
            }
            protected override async Task ExecuteAsync(CancellationToken stoppingToken)
            {
                while (!stoppingToken.IsCancellationRequested)
                {
                    await Task.Delay(10000, stoppingToken);
    
                    using (var scope = _services.CreateScope())
                    {
                        var scopedProcessingService =
                            scope.ServiceProvider
                                .GetRequiredService<IScopedAlertingService>();
    
                        await scopedProcessingService.DoWork(stoppingToken);
                    }
                }
            }
        }