Reputation: 4116
I have a hard time understanding why the behaviour is different to apply the service locator pattern in the Startup Configure method when using the IApplicationBuilder.ApplicationServices
in stead of IServiceProvider
.
When I use IApplicationBuilder.ApplicationServices
(which sets the IServiceProvider
that provides access to the application's service container) I get the following error:
Cannot resolve scoped service '...' from root provider
In contrast, when I use the injected IServiceProvider
directly it works like a charm:
public void Configure(
IApplicationBuilder app, IWebHostEnvironment env, IServiceProvider sp)
{
// 1 : resolving a scoped service directly from application services container
// IServiceProvider throws 'Cannot resolve scoped service from root provider'
var test1 = app.ApplicationServices.GetRequiredService<IMyScopedService>();
// 2 : works like a charm
var test2 = sp.GetRequiredService<IMyScopedService>();
// 3 : also works like a charm
var scope = app.ApplicationServices.CreateScope();
var test3 = scope.ServiceProvider.GetRequiredService<IMyScopedService>();
}
Why is this? Why does the injected IServiceProvider
behave like it has "service scope" and IApplicationBuilder.ApplicationServices
like it has a sort of "application scope" outside of service scopes?
In a way the injected IServiceProvider
works the same as creating a scope first with IApplicationBuilder
and only then resolving the scoped service from the scope's service provider.
My head is spinning now.
Obviously it seems that the scope of IApplicationBuilder
(does one name it like this?) is outside of service scopes. But when I look up IApplicationBuilder I can't find anything about the root provider.
Can anyone clarify this? Did I miss an obvious beginners tutorial somewhere that explains this root provider or application's service container?
Upvotes: 6
Views: 1884
Reputation: 26647
This is by design.
ASP.NET runtime instantiate IServiceProvider
in such way that it prohibits scoped services resolved from the root:
serviceCollection.BuildServiceProvider(validateScopes: true);
app.ApplicationServices
is root IServiceProvider
. sp
is child of app.ApplicationServices
.
You can check this using following line of code in Immediate Window
of Visual Studio, while on a break point (that way you can access internal ServiceProviderEngineScope
directly):
app.ApplicationServices == ((Microsoft.Extensions.DependencyInjection.ServiceLookup.ServiceProviderEngineScope)sp).Engine.Root
// true
So only children of app.ApplicatoinServices
can resolve scoped services. One child sp
was provided as input parameter, 2nd one you created manually.
UPDATE:
I gave a very detailed answer on MVC Core ServiceProvider
and Scope
default behavior here.
Upvotes: 8