Correct way to start a BackgroundService in ASP.NET Core - c#

I have implemented a BackgroundService in an ASP.NET Core 2.1 application:
public class MyBackgroundService : BackgroundService
{
protected override Task ExecuteAsync(CancellationToken stoppingToken)
{
while (true)
{
await DoSomethingAsync();
await Task.Delay(10 * 1000);
}
return Task.CompletedTask;
}
}
I have registered it in my ConfigureServices() method:
services.AddSingleton<MyBackgroundService>();
I am currently (reluctantly) starting it by calling (and not awaiting) the StartAsync() method from within the Configure() method:
app.ApplicationServices.GetService<SummaryCache>().StartAsync(new CancellationToken());
What is the best practice method for starting the long running service?

Explicitly calling StartAsync is not needed.
Calling
services.AddSingleton<MyBackgroundService>();
won't work since all service implementations are resolved via DI through IHostedService interface.
edit:
e.g.
svcProvider.GetServices<IHostedService>() -> IEnumerable<IHostedService>
You need to call either:
services.AddSingleton<IHostedService, MyBackgroundService>();
or
services.AddHostedService<MyBackgroundService>();
edit:
AddHostedService also registers an IHostedService: https://learn.microsoft.com/en-us/dotnet/api/microsoft.extensions.dependencyinjection.servicecollectionhostedserviceextensions.addhostedservice?view=aspnetcore-2.2

Related

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

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);
}
}
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.

Quartz job does not run with autofac and a constructor with a service

I have the Quartz Job shown below
public class ExtractTradesJob: IJob
{
private ITradeExtractor _tradeExtractor;
public ExtractTradesJob(ITradeExtractor tradeExtractor)
{
_tradeExtractor = tradeExtractor;
}
public async Task GetTradesAsync(DateTime dateTime)
{
Console.WriteLine(dateTime);
}
void IJob.Execute(IJobExecutionContext context)
{
Task.Run(async () => await GetTradesAsync(DateTime.Now));
}
}
I have 2 issues
As I need to use .NET 4.5, Async support is not available out of the box so is the method I have used for calling my async method correct?
It appears as though if I have a constructor as in my case, the job does not fire. I have checked and I know the the ITradeExtractor service is registered correctly. So how can I have a job that takes a service in its constructor? If I remove the constructor, my Execute method is called correctly
I am using AutoFac 3.5.2 and Quartz 2.6.2 and AutoFac.Extras.Quartz 3.5.0
I am using the code below to setup AutoFac
public static void RegisterWithAutofac(ContainerBuilder builder)
{
builder.RegisterType<TradeExtractor>()
.As<ITradeExtractor>()
.SingleInstance();
builder.Register(x => new StdSchedulerFactory().GetScheduler()).As<IScheduler>();
}
I know these are old packages, but my limitation of having to use .NET 4.5 means this is out of my control
Paul

Background tasks are being queued and not executed

I've implemented the BackgroundQueue as explained here, and as shown:
public ActionResult SomeAction()
{
backgroundQueue.QueueBackgroundWorkItem(async ct =>
{
//Do some work...
});
return Ok();
}
I registered the BackgroundQueue with Autofac as:
builder.RegisterType<BackgroundQueue>()
.As<IBackgroundQueue>()
.SingleInstance();
So far so good. I call my controller action and the task is added to the queue. And there it stays without being executed.
So how do I get the task to execute?
The BackgroundQueue implementation that you took from the documentation is only one part to the solution: The background queue will just keep track of the jobs that you want to be executed.
What you will also need is right below that in the docs: The QueuedHostedService. This is a background service that gets registered with the DI container and is started when the application starts. From then on, it will monitor your BackgroundQueue and work off jobs as they get queued.
A simplified example implementation of this background service, without logging or error handling, could look like this:
public class QueuedHostedService : BackgroundService
{
private readonly IBackgroundQueue _backgroundQueue;
public QueuedHostedService(IBackgroundQueue backgroundQueue)
{
_backgroundQueue = backgroundQueue;
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
var workItem = await _backgroundQueue.DequeueAsync(stoppingToken);
await workItem(stoppingToken);
}
}
}

How to run multiple BackgroundService parallel in .net core 3.0?

How is it possible to run multiple IHostedServices in parallel?
I use the WorkerService in .Net Core 3.0 and want both services to run parallel. Currently the second service is waiting for the first one to finish. Both services should run endlessly.
public static IHostBuilder CreateHostBuilder(string[] args)
{
return Host.CreateDefaultBuilder(args)
.ConfigureServices((hostContext, services) =>
{
services.AddHostedService<ServiceA>();
services.AddHostedService<ServiceB>();
});
}
A service looks like this:
public class ServiceA : BackgroundService
{
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
do
{
Console.WriteLine("Sample");
await Task.Delay(5000, stoppingToken);
} while (!stoppingToken.IsCancellationRequested);
}
}
// edit:
Very reluctantly I would use a Task.Run(() => method()); method like this. But of course this way always works:
public class ServiceA : BackgroundService
{
public override Task StartAsync(CancellationToken cancellationToken)
{
Task.Factory.StartNew(() => ExecuteAsync(cancellationToken), cancellationToken);
return Task.CompletedTask;
}
}
I asked myself a similar question and made some search but couldn't find a good answer.
I solved the issue running every background service in Task.Run with a cancellation token from BackgroundService.ExecuteAsync()
I have 2 services like you.
public class BackgroundService1: BackgroundService
{
public BackgroundService1()
{
}
protected override Task ExecuteAsync(CancellationToken stoppingToken)
{
Task.Run(async () =>
{
await DoWork(stoppingToken);
}, stoppingToken);
return Task.CompletedTask;
}
}
//Second service is just like the first one:
public class BackgroundService2: BackgroundService
{
public BackgroundService2()
{
}
protected override Task ExecuteAsync(CancellationToken stoppingToken)
{
Task.Run(async () =>
{
await DoWork(stoppingToken);
}, stoppingToken);
return Task.CompletedTask;
}
}
and register them in Program.cs
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureServices((hostContext, services) =>
{
services.AddHostedService<BackgroundService1>();
services.AddHostedService<BackgroundService2>();
})
.UseWindowsService()
I've had the same kind of issue: Multiple service that do different work at different frequencies.
When looking into it, BackgroundService seems to be designed for sequential execution (an infinite loop based service's worst enemy).
After getting a hint from this thread, I found the solution that works for my case using Microsoft's Timer Service example.
The base TimerService implements IHostedService and IAsyncDisposable:
StartAsync() starts the timer on the DoWork()
DoWork() is your overridable main work procedure.
StopAsync() stops the timer gracefully.
DisposeAsync() cleans up.
I've tested by deriving multiple TimerServices with different execution frequencies and adding them with services.AddHostedService<>();.
They all start and run at the same time, do their bit on clock.
/!\ It is not Task based as it uses timer events. Just pointing this out because I've already had quite a difficult troubleshooting experience the one time I mixed time-based events and Tasks /!\
No need to manually create a task. The default StartAsync calls ExecuteAsync and returns that task to be awaited somewhere else.
https://github.com/aspnet/Hosting/blob/master/src/Microsoft.Extensions.Hosting.Abstractions/BackgroundService.cs#L30
So, you can do return base.StartAsync(cancellationToken) before returning Task.Completed in StartAsync.

.NET Core 2.0 IServiceCollection missing AddHostedService?

Trying to use:
Startup.cs
public void ConfigureServices(IServiceCollection services) {
services.AddHostedService<LifetimeEvents>();
.
.
.
}
where the LifeTimeEvents class inherits from IHostedService. I get this error:
'IServiceCollection' does not contain a definition for 'AddHostedService' and no extension method 'AddHostedService' accepting a first argument of type 'IServiceCollection' could be found (are you missing a using directive or an assembly reference?)
I can't seem to find the proper namespace to use or nuget package to include to get this working, but it worked out of the box in .NET Core 2.1, is this just not available in .NET Core 2.0? Is there any way to get it working?
UPDATE:
As a workaround I changed my code to use:
Startup.cs
public void ConfigureServices(IServiceCollection services) {
services.AddSingleton<LifetimeEvents>();
.
.
.
}
public void Configure(IApplicationBuilder appBuilder, IHostingEnvironment envHost, LifetimeEvents appEvents) {
appEvents.StartAsync(new CancellationToken(false));
.
.
.
}
and that seems to have done the job. Doesn't answer my original question, and I'm not sure how "best practices" it is, but it did get me moving refactoring this .NET Core 2.0 app.
is this just not available in .NET Core 2.0?
ServiceCollectionHostedServiceExtensions.AddHostedService(IServiceCollection) Method as shown in the API reference
Applies to
ASP.NET Core
2.1
However the source code is available on GitHub. You can easily check it out there and copy a local version to your 2.0 project
namespace Microsoft.Extensions.DependencyInjection
{
public static class ServiceCollectionHostedServiceExtensions
{
/// <summary>
/// Add an <see cref="IHostedService"/> registration for the given type.
/// </summary>
/// <typeparam name="THostedService">An <see cref="IHostedService"/> to register.</typeparam>
/// <param name="services">The <see cref="IServiceCollection"/> to register with.</param>
/// <returns>The original <see cref="IServiceCollection"/>.</returns>
public static IServiceCollection AddHostedService<THostedService>(this IServiceCollection services)
where THostedService : class, IHostedService
=> services.AddTransient<IHostedService, THostedService>();
}
}
Source code
Ideally you could just update the project to 2.1 where the extension becomes available.
I believe this is a duplicate question to what I've answered before.
Where am I supposed to start persistent background tasks in ASP.NET Core?
Below is the answer, copy + pasted.
I believe you're looking for this
https://blogs.msdn.microsoft.com/cesardelatorre/2017/11/18/implementing-background-tasks-in-microservices-with-ihostedservice-and-the-backgroundservice-class-net-core-2-x/
And i did a 2 hour self-proclaimed-award-winning hackathon against myself to learn abit of that.
https://github.com/nixxholas/nautilus
You can refer the injections here and implement the abstracts from there too.
Many MVC projects are not really required to operate persistent background tasks. This is why you don't see them baked into a fresh new project via the template. It's better to provide developers an interface to tap on and go ahead with it.
Also, with regards to opening that socket connection for such background tasks, I have yet to establish a solution for that. As far as I know/did, I was only able to broadcast payload to clients that are connected to my own socketmanager so you'll have to look elsewhere for that. I'll definitely beep if there is anything regarding websockets in an IHostedService.
Ok anyway here's what happens.
Put this somewhere in your project, its more of an interface for you to overload with to create your own task
/// Copyright(c) .NET Foundation.Licensed under the Apache License, Version 2.0.
/// <summary>
/// Base class for implementing a long running <see cref="IHostedService"/>.
/// </summary>
public abstract class BackgroundService : IHostedService, IDisposable
{
protected readonly IServiceScopeFactory _scopeFactory;
private Task _executingTask;
private readonly CancellationTokenSource _stoppingCts =
new CancellationTokenSource();
public BackgroundService(IServiceScopeFactory scopeFactory) {
_scopeFactory = scopeFactory;
}
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();
}
}
Here's how you can actually use it
public class IncomingEthTxService : BackgroundService
{
public IncomingEthTxService(IServiceScopeFactory scopeFactory) : base(scopeFactory)
{
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
using (var scope = _scopeFactory.CreateScope())
{
var dbContext = scope.ServiceProvider.GetRequiredService<NautilusDbContext>();
Console.WriteLine("[IncomingEthTxService] Service is Running");
// Run something
await Task.Delay(5, stoppingToken);
}
}
}
}
If you noticed, there's a bonus there. You'll have to use a servicescope in order to access db operations because its a singleton.
Inject your service in
// Background Service Dependencies
services.AddSingleton<IHostedService, IncomingEthTxService>();

Categories