To solve this, we can use IServiceScopeFactory to create a scope for each background task. This allows us to resolve the scoped service properly within the background service, ensuring it’s disposed correctly.
Here’s an updated example of how to implement this in a modern .NET application:
public class MyBackgroundService : IHostedService
{
private readonly IServiceScopeFactory _scopeFactory;
private readonly ILogger<MyBackgroundService> _logger;
public MyBackgroundService(IServiceScopeFactory scopeFactory, ILogger<MyBackgroundService> logger)
{
_scopeFactory = scopeFactory;
_logger = logger;
}
public Task StartAsync(CancellationToken cancellationToken)
{
// Create a scope for each background task
var scope = _scopeFactory.CreateScope();
var myScopedService = scope.ServiceProvider.GetRequiredService<MyScopedService>();
// You can now use myScopedService as needed
_logger.LogInformation("Background service started.");
return Task.CompletedTask;
}
public Task StopAsync(CancellationToken cancellationToken)
{
_logger.LogInformation("Background service stopping.");
return Task.CompletedTask;
}
}
Key Changes for .NET 6/7:
- Minimal Hosting Model: The application startup code has changed in .NET 6 and 7, favoring the use of a simplified
Program.cs. - Service Registration: Make sure
MyScopedServiceis registered with a scoped lifetime inConfigureServices:
builder.Services.AddScoped<MyScopedService>();
builder.Services.AddHostedService<MyBackgroundService>();
Best Practices:
- Error Handling: Implement robust error handling within your background services to prevent failures.
- Logging: Use
ILogger<T>for effective logging, especially in background services that may run without direct user interaction. - Cancellation Token: Always respect the
CancellationTokenpassed to theStartAsyncandStopAsyncmethods to gracefully handle shutdowns.
Performance Considerations:
Creating a scope for each background task may introduce some overhead. If possible, try to balance between scoped services and singleton services, depending on your use case.
For more advanced setups, consider using IServiceProvider directly or other patterns like IBackgroundTaskQueue for better task management.
Hi – I’m battling with IHostedService at this very moment!
Would you use IHostedService to execute a long running task from an action method, or should IHostedService only be used for periodic tasks that run in the background? If you can execute from an action method, how would you pass additional parameters to ExecuteAsync?
LikeLiked by 1 person
Hi Robin, basically would use the ExecuteAsync method to ‘administer’ your long running process. When using the BackgroundService class from the framework you are not able to change the parameters. In the blog post https://pgroene.wordpress.com/2018/05/31/run-scheduled-background-tasks-in-asp-net-core you can find some samples to implement the IHostedService interface yourself. If possible I would try to cut the ‘long’ running process into smaller parts to have more control over what is happening and for example handle when the application shuts down.
LikeLike
Hi Peter, I’m facing with the same issue. Can you tell me is this independent class or this should be a part of startup class?
For scheduled task, I re-used your solution that you provided on GitHub.
Thanks in advance!
LikeLike
When you start a new task from a scheduler, it does not have a scope. If you want to use classes that run in a scope like a dbconnection you should add the:
using (var scope = _serviceScopeFactory.CreateScope())
{
IScoped scoped = scope.ServiceProvider.GetRequiredService();
//Do your stuff
}
In the Do Your Stuff part you can use the scope.ServiceProvider.GetRequiredService to create the top level obect in you your scope that will do the processing.
LikeLike
Hi, Peter.
Thank you very much for your time and answer!
Please take a look of this image: https://ibb.co/YjTLwPg
I added this part of the code in ScheduleTask.cs but, I’m getting the error with a message: ‘The type arguments for method ‘ServiceProviderServiceExtensions.GetRequiredService(IServiceProvider)’ cannot be inferred from the usage. Try specifying the type arguments explicitly. ‘.
I tried to find solution, but with no succes.
Thanks in advance!
LikeLike
Thanks for creating this post. It was exactly what I needed!
LikeLike
Hello Peter,
Like other readers… I cannot thank you enough for this short but very crucial post! I was struggling to use Asp.Net Identity from insider a Worker Service project because of this exact situation – The Worker is registered as a Singleton while the UserManager of Identity services is registered as Scoped and your post helped me bridge the gap.
Again thank you very much for this public service to the developer community!!
Kind Regards…
PS: Hope you keep up the good work so minions like me can learn from folks like you!
LikeLike
Thank you for you comment!
LikeLike