I want to run a background job in ASP.NET Core 2.1. It has to run every 2 hours and it will need to access my DI Container because it will perform some cleanups in the database. It will need to be async
and it should run independently of my ASP.NET Core 2.1 application.
I saw that there was an IHostedService
, but ASP.NET Core 2.1 also introduced an abstract class called BackgroundService
that does some more work for me. Seems good, I want to use that!
I have not been able to figure out how run a service that derived from BackgroundService
on a timer, though.
Do I need to configure this in the ExecuteAsync(token)
by remembering the last time it ran and figuring out if this was 2 hours, or is there a better/cleaner way to just say somewhere that it has to run every 2 hours?
Also, is my approach to my problem correct with an BackgroundService
?
Thank you!
Edit:
Posted this on the MS extensions repo
Updated 03-2022, read it on the bottom!
Updated 04-2020, read it on the bottom!
@Panagiotis Kanavos gave an answer in the comments of my question but it did not post it as an actual answer; this answer is dedicated to him/her.
I used a Timed background service like the one from Microsoft docs to create the service.
internal class TimedHostedService : IHostedService, IDisposable
{
private readonly ILogger _logger;
private Timer _timer;
public TimedHostedService(ILogger<TimedHostedService> logger)
{
_logger = logger;
}
public Task StartAsync(CancellationToken cancellationToken)
{
_logger.LogInformation("Timed Background Service is starting.");
_timer = new Timer(DoWork, null, TimeSpan.Zero,
TimeSpan.FromSeconds(5));
return Task.CompletedTask;
}
private void DoWork(object state)
{
_logger.LogInformation("Timed Background Service is working.");
}
public Task StopAsync(CancellationToken cancellationToken)
{
_logger.LogInformation("Timed Background Service is stopping.");
_timer?.Change(Timeout.Infinite, 0);
return Task.CompletedTask;
}
public void Dispose()
{
_timer?.Dispose();
}
}
In my case I made the _timer
call async by doing new Timer(async () => await DoWorkAsync(), ...)
.
In the future, an extension could be written that makes a class like this available in the Extensions repo because I think this is quite useful. I posted the github issue link in the description.
A tip, if you plan on reusing this class for multiple hosted services, consider creating a base class that contains the timer and an abstract PerformWork()
or something so the "time" logic is only in one place.
Thank you for your answers! I hope this helps someone in the future.
Update 04-2020:
Injecting a scoped service in here is not possible with the normal Core service collection DI container, out of the box. I was using autofac which made it possible to use scoped services like IClassRepository
in the constructor because of wrong registration, but when I started working on a different project that used only AddScoped<>(), AddSingleton<>(), AddTransient<>()
we figured out that injecting scoped things do not work because you are not in a scoped context.
In order to use your scoped services, inject a IServiceScopeFactory
(Easier to test with) and use CreateScope()
which allows you to use scope.GetService()
with a using
statement :)
Update 03-2022: This post has gotten LOTS of views and attention, but I have to say I am no longer a big fan of my solution. I would propose different solutions:
The downsides of the solution posted in this answer are:
async
support in this solution. I never really figured out if this solution is "correct"Quartz.Net
does support this.If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With