Skip to content

Conversation

@rosebyte
Copy link
Member

fixes #108874

DI filters out open generics with incompatible constraints by default. However, if the most recently registered open generic has incompatible constraints, DI skips it during IEnumerable resolution and falls back to the previous compatible registration. If that earlier registration is compatible, DI caches it as the “last” valid one. As a result, when users resolve a service whose most recent implementation had incompatible constraints, but an earlier one was compatible, they receive an implementation that is not the last registered. This breaks the usual “last wins” rule of DI containers and can lead to confusing, nearly impossible to debug behaviour.

@rosebyte
Copy link
Member Author

@dotnet-policy-service agree

@dotnet-policy-service
Copy link
Contributor

Tagging subscribers to this area: @dotnet/area-extensions-dependencyinjection
See info in area-owners.md if you want to be subscribed.

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR fixes a bug in the DI container where resolving a single service instance (GetService) could incorrectly return an earlier compatible open generic registration when the most recent registration had incompatible generic constraints, violating the "last wins" principle.

Changes:

  • Updated CallSiteFactory to track the slot counter even when the last open generic registration has incompatible constraints, ensuring single service resolution correctly attempts to use the last registration and throws ArgumentException
  • Added a test case to verify that incompatible constrained open generics are skipped in enumerable resolution but cause exceptions in single service resolution

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.

File Description
src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/CallSiteFactory.cs Updated slot tracking logic to include the case where the last registration has incompatible constraints, ensuring proper "last wins" semantics
src/libraries/Microsoft.Extensions.DependencyInjection/tests/DI.Tests/ServiceProviderContainerTests.cs Added test case to verify the fix works correctly for both enumerable and single service resolution scenarios

Copy link
Member

@CarnaViire CarnaViire left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@rosebyte rosebyte merged commit 982c925 into dotnet:main Jan 23, 2026
89 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[API Proposal]: DI container should respect generic constraints when resolving single instance.

3 participants