Skip to content

feat(VariantBasedInjection): LazyVariantServiceProvider with Keyed DI#605

Open
Stepami wants to merge 9 commits into
microsoft:mainfrom
Stepami:feature/keyed-di
Open

feat(VariantBasedInjection): LazyVariantServiceProvider with Keyed DI#605
Stepami wants to merge 9 commits into
microsoft:mainfrom
Stepami:feature/keyed-di

Conversation

@Stepami
Copy link
Copy Markdown

@Stepami Stepami commented May 14, 2026

new class LazyVariantServiceProvider uses sp to get keyed services

Why this PR?

Keyed DI can be used in FeatureManagement library as package-provided solution despite netstandard targeting

Visible Changes

  • LazyVariantServiceProvider class
  • WithLazyVariantService di extension method

VariantServiceProvider uses sp to get keyed services

BREAKING CHANGE: behavioural: keyed services registration needed unless
we override descriptors for backward comp
@Stepami
Copy link
Copy Markdown
Author

Stepami commented May 14, 2026

@microsoft-github-policy-service agree

rename keyedServiceProvider to serviceProvider for clarity
@Stepami
Copy link
Copy Markdown
Author

Stepami commented May 15, 2026

I just thought that to avoid BC i could introduce LazyVariantServiceProvider class with keyed di as new implementation of IVariantServiceProvider. Then add WithLazyVariantService extension method. That could allow mark current implementation as obsolete to prepare consumers

Минин Степан Александрович added 4 commits May 19, 2026 15:26
Extension method impl && tests

Closes microsoft#564
Added notes about impls handling to highlight difference with lazy one
@Stepami Stepami changed the title [proof of concept] feat(VariantBasedInjection): Keyed DI PoC feat(VariantBasedInjection): Keyed DI PoC May 19, 2026
@Stepami
Copy link
Copy Markdown
Author

Stepami commented May 19, 2026

@zhiyuanliang-ms could you please take a look?

Managed to avoid BC through making Keyed DI implementation as the new feature

@Stepami Stepami changed the title feat(VariantBasedInjection): Keyed DI PoC feat(VariantBasedInjection): LazyVariantServiceProvider with Keyed DI May 19, 2026
Минин Степан Александрович added 2 commits May 20, 2026 13:01
@zhiyuanliang-ms
Copy link
Copy Markdown
Member

zhiyuanliang-ms commented May 26, 2026

Hey, @Stepami

Thank you for your contribution!

The implementation looks good to me overall. Instead of introducing a new API like WithLazyVariantService, I think we should consider updating the existing WithVariantService API and make this behavior configurable, for example:

builder.Services.AddFeatureManagement()
    .WithVariantService<ICalculator>("Calculator", useKeyedService: true);

My only concern is around compatibility. Keyed services are a .NET 8 DI feature, as I mentioned in this comment, while we still have customers running on .NET Framework. We have been hitted by a similar compatibility issue before #435

From a compilation perspective, using the keyed DI APIs should be fine since the 8.x DependencyInjection abstractions package supports netstandard.

My concern is runtime compatibility. Although GetKeyedService is exposed as an extension method on IServiceProvider, it only works when the actual provider implements IKeyedServiceProvider. Otherwise, it throws an InvalidOperationException indicating that keyed services are not supported.

Because of that, this is not necessarily a .NET Framework issue by itself. It can work on .NET Framework if the application uses a compatible Microsoft.Extensions.DependencyInjection 8.x service provider. The risk is for consumers using a custom provider.

I think the existing non-keyed behavior should remain the default, and the keyed DI path should be explicitly opt-in with clear validation/error messaging when the current service provider does not support IKeyedServiceProvider.

image

@zhiyuanliang-ms
Copy link
Copy Markdown
Member

cc @jimmyca15 @linglingye001

@Stepami
Copy link
Copy Markdown
Author

Stepami commented May 26, 2026

Hey, @zhiyuanliang-ms

Thanks for the feedback! I like your proposal.

To make things clear i have a few questions:

  • How do I update WithVariantService API: create new overload or update the current one?
  • ServiceProvider is not built during ServiceCollection phase, so we will only find out whether it is keyed or not during service instantiation. Do we really need the validation other than that InvalidOperationException?

@zhiyuanliang-ms
Copy link
Copy Markdown
Member

Hey, @Stepami

How do I update WithVariantService API: create new overload or update the current one?

I think we should add a new overload. Because replacing the existing one with WithVariantService(string, bool = false) would be source-compatible but binary-breaking for already compiled consumers.

ServiceProvider is not built during ServiceCollection phase, so we will only find out whether it is keyed or not during service instantiation. Do we really need the validation other than that InvalidOperationException?

To be honest, I don't have a perfect answer now.

My current thinking is that best effort is probably enough. If users explicitly enable the keyed-service path, the happy path is straightforward: they are expected to register their services with AddKeyedXXX and use a service provider that supports keyed services. In that case, everything should work as expected.

The unhappy path is where the user enables useKeyedService: true, but the actual runtime service provider does not support IKeyedServiceProvider, or the keyed service was not registered with the expected key. Since we cannot reliably validate this during the ServiceCollection phase, I think the best we can do is catch the generic DI exception and re-throw it with "feature management"-specific context

- introduced new WithVariantService overload which replaced
WithLazyVariantService ext method
- added type check for sp to be keyed in case of lazy variant sp

Closes microsoft#564
@Stepami
Copy link
Copy Markdown
Author

Stepami commented May 26, 2026

@zhiyuanliang-ms

I decided to express the intentions around compatibility through typing in LazyVariantServiceProvider.

Now ctor accepts IKeyedServiceProvider. If sp is not keyed then we can't instantiate LazyVariantServiceProvider and tell consumers about it with exception

@zhiyuanliang-ms
Copy link
Copy Markdown
Member

zhiyuanliang-ms commented May 27, 2026

Hey, @Stepami

Could you take a look at #606

Yesterday, I discussed your PR with @jimmyca15 and we realized that we can have the current VariantServiceProvider to take service provider directly and check whether keyed service is available during the runtime. Then, there is no need to have a new LazyVariantServiceProvider class. But we can have fallback logic so that the configurable option useKeyedService can be saved as well.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants