0

This is related to the following, but it is not exactly the same: EF. The connection was not closed. The connection's current state is connecting

I understand this error can be the result of a race condition when dealing with DbContext.

Here is a simplified example of what was causing me problems, and my solution to fix it. I just don't quite understand why my solution works:

Startup.cs

services.AddDbContext<MyDbContext>(options => options.UseSqlServer("ConnString"));

// This will cause the "Connection was not closed..." error.
services.AddSingleton<IHostedService, SomeBackgroundService>(provider => 
    new SomeBackgroundService(provider.GetRequiredService<MyDbContext>());

// Instead, I instantiate the DbContext here instead of letting DI do it
// and this eliminates the error.
services.AddSingleton<IHostedService, SomeBackgroundService(provider =>
    new SomeBackgroundService(new MyDbContext(
        new DbContextOptionsBuilder<MyDbContext>().UseSqlServer("ConnString").Options));

Inside of my SomeBackgroundService I execute some asynchronous queries, while at the same time other queries are being executed inside controller methods.

However, that being the case, shouldn't using provider.GetRequiredService<T> instantiate a new DbContext in the same way?

K. Akins
  • 657
  • 1
  • 7
  • 12
  • 5
    You should never use a singleton in conjunction with a `DbContext`. – DavidG Jun 27 '18 at 15:47
  • Thank you for your reply. I guess my question was more along the lines of, even if I _do_ have a singleton, I figured provider.GetRequiredService would create an instance of DbContext to be injected into my singleton class? – K. Akins Jun 29 '18 at 13:52
  • Yes, which effectively makes that `DbContext` also a singleton. Now, if you try to use that across HTTP requests, it will explode. – DavidG Jun 29 '18 at 13:53
  • I see what you're saying. Forgive me though, I'm still a little confused. The `IHostedService` that uses the DbContext is a background task, so my thought is that the background task is not really being "accessed" across HTTP requests, just running in the background. Are you saying that `provider.GetRequiredService` used within the context of registering a singleton will not actually create a DbContext instance, but fetch an existing DbContext registered with the DI container -- which would be based on `services.AddDbcontext`? – K. Akins Jun 29 '18 at 17:43

1 Answers1

2

The official documentation has examples on how to use scoped services within a hostes service https://learn.microsoft.com/en-us/aspnet/core/fundamentals/host/hosted-services?view=aspnetcore-2.1#consuming-a-scoped-service-in-a-background-task.

TL;DR You inject the IServiceProvider (which is always available) into your IHostedService implementation, then create a scope per invocation and resolve the DbContext from there.

davidfowl
  • 37,120
  • 7
  • 93
  • 103
  • Thank you David, I believe that makes sense to me now. So am I right to say that `services.AddDbContext` registers the DbContext as a scoped service? I will give this a shot, thanks again! – K. Akins Jul 02 '18 at 15:12