2

I have a problem when try to use database context in one Singleton Service in my app.

App.cs

...
var service = builder.Services;
service.AddDbContext<MyDBContext>(options => options.UseSqlite(connectionString));
service.AddSingleton<MyMonitorManager>();
...

MyDBContext.cs

public class MyDBContext : ApiAuthorizationDbContext<ApplicationUser>
  {
    public DroneDBContext(...): base(options, operationalStoreOptions){}
    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder){}
    protected override void OnModelCreating(ModelBuilder modelBuilder){}
  }

MyMonitorManager.cs

public class MyMonitorManager {
   private readonly MyDBContext _databaseManager;
   ...
}

There are errors:

Cannot consume scoped service 'MyController.MyDBContext' from singleton 'MyController.Service.MyMonitorManager'.

I try to search here and think about use like this:

MyDBContext dbContext = new MyDBContext();
service.AddSingleton(new MyMonitorManager(dbContext));

But not sure this is how I should do in .NET 6

Guru Stron
  • 102,774
  • 10
  • 95
  • 132
Dina
  • 146
  • 2
  • 13
  • 1
    Check this link https://stackoverflow.com/questions/45810851/cannot-consume-scoped-service-imongodbcontext-from-singleton-iactiveusersservice – Ertugrul Sungur Sep 06 '22 at 08:30
  • 1
    This is explained in [Use scoped services within a BackgroundService](https://learn.microsoft.com/en-us/dotnet/core/extensions/scoped-service) – Panagiotis Kanavos Sep 06 '22 at 08:40

2 Answers2

4

You can use IServiceScopeFactory in your singleton class to create a new scope, and then get a db context reference from that:

public class MyMonitorManager {
    private readonly IServiceScopeFactory _scopeFactory;
 
     public MyMonitorManager(IServiceScopeFactory _scopeFactory)
     {
        _scopeFactory = scopeFactory;
     }
 
     public SomeMethod()
     {
        using (var scope = _scopeFactory.CreateScope())
        {
            var db = scope.ServiceProvider.GetRequiredService<MyDBContext>();
            ...
        }
     }
}
John M
  • 2,510
  • 6
  • 23
  • 31
3

You can't inject directly scoped service into singleton, you have follwoing options:

  1. Register database context with different lifetime, though in general case it is not the best option:
service.AddDbContext<MyDBContext>(options => options.UseSqlite(connectionString), ServiceLifetime.Transient);
  1. Inject IServiceScopeFactory to create scope and resolve context when needed the singleton service:
public class MyMonitorManager 
{
   private readonly IServiceScopeFactory _scopeFactory; 
   public class MyMonitorManager(IServiceScopeFactory scopeFactory) => _scopeFactory = scopeFactory;

    public void SomeMethod()
    {
        using (var scope = _serviceScopeFactory.CreateScope())
        {
            var ctx = scope.ServiceProvider.GetService<MyDBContext>(); 
            // use context
        }
    }
}
Guru Stron
  • 102,774
  • 10
  • 95
  • 132