Skip to content

Support contextual variant service provider and feature status fallback - #611

Open
Zhiyuan Liang (zhiyuanliang-ms) wants to merge 2 commits into
mainfrom
zhiyuanliang/contextual-vsp
Open

Support contextual variant service provider and feature status fallback#611
Zhiyuan Liang (zhiyuanliang-ms) wants to merge 2 commits into
mainfrom
zhiyuanliang/contextual-vsp

Conversation

@zhiyuanliang-ms

@zhiyuanliang-ms Zhiyuan Liang (zhiyuanliang-ms) commented Jul 29, 2026

Copy link
Copy Markdown
Member

Why this PR?

#604 #608

IVariantServiceProvider<TService>.GetServiceAsync currently does not accept a context, which limits it to scenarios that rely on ambient context.

This creates a capability gap between VariantServiceProvider and IVariantFeatureManager, because both IVariantFeatureManager.IsEnabledAsync and IVariantFeatureManager.GetVariantAsync provide overloads that accept an explicit context.

How to fix

This PR introduces IContextualVariantServiceProvider<TService> with method GetServiceAsync<TContext>(TContext context, CancellationToken cancellationToken)

The PR also adds WithVariantService<TService, TEnabled, TDisabled>(featureName) which supports falling back to a service based on the feature’s enabled or disabled status.

Service resolution behavior

Service resolution follows these steps:

  1. Resolve the assigned variant.
  • When the supplied context implements ITargetingContext, pass it to GetVariantAsync.
  • Otherwise, resolve the variant without an explicit context.
  • GetVariantAsync only accepts ITargetingContext, so a non-targeting context is not used during variant resolution.
  1. If a variant is assigned and a matching service is registered, return that service.
  2. Otherwise, fall back to the feature status.
  • If the feature is enabled, return TEnabled.
  • If the feature is disabled, return TDisabled.

Because IsEnabledAsync accepts an arbitrary TContext, the feature-status fallback honors the supplied context even when it does not implement ITargetingContext.

Copilot AI review requested due to automatic review settings July 29, 2026 09:46
Comment thread tests/Tests.FeatureManagement/VariantServiceProviderTest.cs Fixed
Comment thread src/Microsoft.FeatureManagement/VariantServiceProvider.cs Dismissed

Copilot AI left a comment

Copy link
Copy Markdown

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 expands the variant-service injection capabilities in the Feature Management .NET SDK by introducing a contextual variant service provider API and adding an optional “feature enabled/disabled” fallback path when no variant-based service can be resolved.

Changes:

  • Introduces IContextualVariantServiceProvider<TService> and updates the internal VariantServiceProvider<TService> to support context-aware evaluation.
  • Adds WithVariantService<TService, TEnabled, TDisabled>(featureName) to allow falling back to an enabled/disabled implementation when variant resolution fails.
  • Refactors and extends tests into a dedicated VariantServiceProviderTest suite, including new coverage for contextual behavior and feature-status fallback.

Reviewed changes

Copilot reviewed 7 out of 7 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
tests/Tests.FeatureManagement/VariantServices.cs Adds alias-based test implementations used by new variant/fallback tests.
tests/Tests.FeatureManagement/VariantServiceProviderTest.cs New test suite covering variant DI, keyed service resolution, contextual provider behavior, and status fallback.
tests/Tests.FeatureManagement/FeatureManagementTest.cs Removes variant service provider tests that were moved to the new dedicated test file.
tests/Tests.FeatureManagement/AppContext.cs Adds an additional context type for tests (currently unused).
src/Microsoft.FeatureManagement/VariantServiceProvider.cs Implements contextual service retrieval and optional feature-status fallback.
src/Microsoft.FeatureManagement/IContextualVariantServiceProvider.cs New public interface for contextual variant service providers.
src/Microsoft.FeatureManagement/FeatureManagementBuilderExtensions.cs Adds new DI builder overload enabling feature-status fallback and registers contextual provider interface.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/Microsoft.FeatureManagement/VariantServiceProvider.cs
Comment thread src/Microsoft.FeatureManagement/FeatureManagementBuilderExtensions.cs Outdated
Comment thread src/Microsoft.FeatureManagement/IContextualVariantServiceProvider.cs Outdated
Comment thread tests/Tests.FeatureManagement/AppContext.cs Outdated
@zhiyuanliang-ms

Copy link
Copy Markdown
Member Author

Hey Степан (@Stepami), do you have any concern about this PR?

@Stepami

Copy link
Copy Markdown

hey Zhiyuan Liang (@zhiyuanliang-ms)! i'll take a look today, been busy for the last few weeks

Comment on lines +119 to +126
Type implementationType = enabled ? _fallbackWhenEnabled : _fallbackWhenDisabled;

if (implementationType != null)
{
return _variantServiceCache.GetOrAdd(GetVariantServiceName(implementationType), ResolveVariantService);
}

return null;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

so if i want to obtain service based on status from keyed do i need to register it by string name?

services.AddSingleton<IService, EnabledService>(nameof(EnabledService));

i would like to update docs in this PR

Comment on lines +110 to 117
if (useContext)
{
enabled = await _featureManager.IsEnabledAsync(_featureName, context, cancellationToken);
}
else
{
enabled = await _featureManager.IsEnabledAsync(_featureName, cancellationToken);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

do we really need this branching here?

await _featureManager.IsEnabledAsync(_featureName, cancellationToken)

is basically

await _featureManager.IsEnabledAsync<object>(_featureName, null, cancellationToken)

useContext is internal configuration, so a developer can't change it

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.

4 participants