A colleague contacted me with the following problem; when running his ASP.NET Core application it failed with the following error message:
Cannot resolve IApiLoggingService from root provider because it requires scoped service NHibernate.IInterceptor
In this post I walk you through the different steps we took to investigate the issue and explain how we solved it.
But before I dive into the problem itself I first want to give some background info on dependency injection in ASP.NET Core and service lifetimes.
Dependency injection and service lifetimes
ASP.NET Core supports the dependency injection (DI) software design pattern, which is a technique for achieving Inversion of Control (IoC) between classes and their dependencies.
Registration of a dependency is done in the built-in service container, IServiceProvider. Services are typically registered at the app's start-up and appended to an IServiceCollection. Once all services are added, you use BuildServiceProvider to create the service container.
Injection of the service is done into the constructor of the class where it's used. The framework takes on the responsibility of creating an instance of the dependency and disposing of it when it's no longer needed.
Services can be registered with one of the following lifetimes:
- Transient: A service is created each time they’re requested from the service container
- Scoped: A service is created once per client request(typically an HTTP request in ASP.NET Core)
- Singleton: A service is created once per lifetime of the service container
Diving into our problem
If we take the knowledge above and apply it to the error message we got, we could assume that the following is happening:
We have registered two services:
- An IApiLoggingService with either a transient or singleton lifetime
- An IInterceptor with a scoped lifetime
The IApiLoggingService has a dependency on the IInterceptor. We are resolving the IApiLoggingService from the root container(the root container is used when there is not a scoped context (e.g. a specific HTTP request) available.
As there is no scope available, it is not possible to create the IInterceptor service with a scoped lifetime.
Remark: The error is caused by the built-in scope validation. If a scoped service is created in the root container, the service's lifetime is effectively promoted to singleton which is of course not what we want. Scope validation prevents this.
Let’s dive into the application to confirm this…
We first take a look at the service registrations:
And indeed we have both a transient and scoped registration.
Now we look at the ApiLoggingService
implementation and we see that the IInterceptor
is injected as a dependency:
And then we check where the IApiLoggingService is injected:
We see that it is used inside an ASP.NET Core middleware and is injected into the constructor. Convention based middleware is created and instantiated before a specific request arrives so that explains the error above.
Does this mean that we cannot use scoped services inside ASP.NET Core middleware?
Fortunately we can use scoped services inside our middleware. Instead of injecting our dependency through constructor injection, we could inject our service in the Invoke method through method injection when using convention based middleware:
Another option is to switch to factory based middleware which I’ll explain in my next post.