If you want to use a scoped service in a singleton service in a Asp.Net Core MVC project you can’t inject it via constructor injection. You’ll have to create a scope when needed. In my case I needed to use a DbContext service regisered with .AddDbContext as a scoped service in startup.cs into a hosted service.
First you have to create a service inheriting of IHostedService (Microsoft.Extensions.Hosting) with the following code:
public abstract class HostedService : IHostedService { // Example untested base class code kindly provided by David Fowler: https://gist.github.com/davidfowl/a7dd5064d9dcf35b6eae1a7953d615e3 private Task _executingTask; private CancellationTokenSource _cts; public Task StartAsync(CancellationToken cancellationToken) { // Create a linked token so we can trigger cancellation outside of this token's cancellation _cts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken); // Store the task we're executing _executingTask = ExecuteAsync(_cts.Token); // If the task is completed then return it, otherwise it's running return _executingTask.IsCompleted ? _executingTask : Task.CompletedTask; } public async Task StopAsync(CancellationToken cancellationToken) { // Stop called without start if (_executingTask == null) { return; } // Signal cancellation to the executing method _cts.Cancel(); // Wait until the task completes or the stop token triggers await Task.WhenAny(_executingTask, Task.Delay(-1, cancellationToken)); // Throw if cancellation triggered cancellationToken.ThrowIfCancellationRequested(); } // Derived classes should override this and execute a long running method until // cancellation is requested protected abstract Task ExecuteAsync(CancellationToken cancellationToken); }
The following code in the singleton service works for me:
public class AnyService
{
private readonly IServiceScopeFactory _scopeFactory;
public AnyService(IServiceScopeFactory scopeFactory)
{
_scopeFactory = scopeFactory;
}
public void Execute()
{
using (var scope = _scopeFactory.CreateScope())
{
var dbContext = scope.ServiceProvider.GetRequiredService<MyDbContext>();
…
}
}
…
}