Skip to content

Implicit assumption in DI behavior when injecting IServiceProvider into scoped or transient services #63225

Closed
@lord-executor

Description

@lord-executor

I came across this issue after a couple of hours of debugging an issue with the Blazor (.NET 6) service configuration. There seems to be an implicit assumption that is currently not covered by any of the existing specification / compliance tests in DependencyInjectionSpecificationTests: When an instance of IServiceProvider is requested from a scoped (non-root) service provider or a non-singleton instance that is created from such a service provider depends on an IServiceProvider then that scoped service provider is resolved and not the root service provider.

There is already a test for singletons that is DependencyInjectionSpecificationTests.SingletonServiceCanBeResolvedFromScope which works differently and always expects the "root service provider" to be injected.

As far as I can tell, the expected behavior is roughly this:

  • If an IServiceProvider is requested, then that should normally resolve to the service provider instance on which that request is made, meaning that the scope of the injected service provider is preserved, unless...
  • If an IServiceProvider is injected into a singleton service, then the root service provider should be resolved because singletons are always "attached" to the root service provider to control their lifetime and injecting a scoped service provider instance would be pretty dangerous.

To check if I was the only person that was unaware of this implicit assumption, I added a new test to cover that and got the following results: Of the 8 currently included DI frameworks, 4 conform to this implicit assumptions and 4 fail the new tests.

Grace => FAIL
Autofac => FAIL
LightInject => FAIL
StashBox => FAIL
DryIoc => OK
StructureMap => OK
Lamar => OK
Unity => OK

At this point, I can almost guarantee that any DI container that doesn't satisfy this implicit assumption will run into problems when running even the basic Blazor project template. Particularly if that container does cover the singleton special case which seems to be true for all of the failing containers. Since that will end up in the same case I had issues with my Ninject integration where services are incorrectly resolved using the root service provider instead of the scoped service provider.

I can give more details on this exact Blazor scenario if needed - it mostly revolves around the Microsoft.AspNetCore.Components.NavigationManager.

Of course, like all critical assumptions of Microsoft.Extensions.DependencyInjection, this one should also be covered by a compliance test and I will create a PR for that and update the tests for the currently failing external DI containers to exclude this test case using the SkippableDependencyInjectionSpecificationTests mechanism.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions