1

I cannot consume a scoped service in a singleton on my system - but on azure it works after publishing.

In my Net 5 service I need to write into my SQL-DB from an azure ServiceBus listener. Do do this, I use 'service.AddDbContext' for the DB and 'service.AddSingleton'for the ServiceBus listener. The listener-services gets the dbContext in the constructor. Since I always deployed the service to azure to test it, I didn't get this error until I tried to start (debug) the exact same service on my local machine.

What does Azure different? I need the listener to be started immediately. And it won't start if it is scoped.

ElFrogo
  • 31
  • 2

1 Answers1

1

When running in developer mode, the ASP.NET Core configuration system (MS.DI) prevents the injection of Scoped dependencies (directly or indirectly) in Singleton consumers. It does this with its Scope-Validation feature. It does so because injecting Scoped dependencies typically have state and such Scoped dependency will be kept alive by the Singleton. This is a common pitfall named Captive Dependencies.

MS.DI, however, only performs this check in debug mode; probably because this check takes time. This means that after you deployed your application, MS.DI will not check for this kind of pitfall, because in that case the application is not running in development mode any longer.

But even though there's no exception, and you think "it works after publishing", you should prevent the injection of short-lived dependencies (such as Scoped dependencies) into long-lived dependencies (Singletons), because this can cause a myriad of hard to find bugs, especially with DbContext instances, because they are not thread-safe and their data get stale very soon.

This means that, whenever possible, lower the lifetime of the consumer (your listener) to either Scoped or Transient. In case the application only has one single instance of that listener, however, lowering the lifestyle has no effect; the instance will be inherently Singleton, no matter witch lifestyle to give it. In that case, the listener should become part of the Composition Root so it can manage IServiceScopes directly. This way you can resolve DbContext instances from a new IServiceScope every time the listener gets invoked.

Steven
  • 166,672
  • 24
  • 332
  • 435