OpenFeature.Providers.MultiProvider
2.11.0
Prefix Reserved
dotnet add package OpenFeature.Providers.MultiProvider --version 2.11.0
NuGet\Install-Package OpenFeature.Providers.MultiProvider -Version 2.11.0
<PackageReference Include="OpenFeature.Providers.MultiProvider" Version="2.11.0" />
<PackageVersion Include="OpenFeature.Providers.MultiProvider" Version="2.11.0" />
<PackageReference Include="OpenFeature.Providers.MultiProvider" />
paket add OpenFeature.Providers.MultiProvider --version 2.11.0
#r "nuget: OpenFeature.Providers.MultiProvider, 2.11.0"
#:package OpenFeature.Providers.MultiProvider@2.11.0
#addin nuget:?package=OpenFeature.Providers.MultiProvider&version=2.11.0
#tool nuget:?package=OpenFeature.Providers.MultiProvider&version=2.11.0
OpenFeature .NET MultiProvider
The MultiProvider is a feature provider that enables the use of multiple underlying providers, allowing different providers to be used for different flag keys or based on specific routing logic. This enables scenarios where different feature flags may be served by different sources or providers within the same application.
Overview
The MultiProvider acts as a composite provider that can delegate flag resolution to different underlying providers based on configuration or routing rules. It supports various evaluation strategies to determine how multiple providers should be evaluated and how their results should be combined.
For more information about the MultiProvider specification, see the OpenFeature Multi Provider specification.
Installation
dotnet add package OpenFeature.Providers.MultiProvider
Usage
Dependency Injection Setup (Recommended)
The MultiProvider integrates seamlessly with the OpenFeature dependency injection system, allowing you to configure multiple providers using the AddMultiProvider extension method:
using OpenFeature.Providers.MultiProvider.DependencyInjection;
builder.Services.AddOpenFeature(featureBuilder =>
{
featureBuilder
.AddMultiProvider("multi-provider", multiProviderBuilder =>
{
// Add providers using factory methods for proper DI integration
multiProviderBuilder
.AddProvider("primary", sp => new YourPrimaryProvider())
.AddProvider("fallback", sp => new YourFallbackProvider())
.UseStrategy<FirstMatchStrategy>();
});
});
// Retrieve and use the client
var featureClient = openFeatureApi.GetClient("multi-provider");
var result = await featureClient.GetBooleanValueAsync("my-flag", false);
Adding Providers with DI
The MultiProviderBuilder provides several methods to add providers:
Using Factory Methods:
multiProviderBuilder
.AddProvider("provider-name", sp => new InMemoryProvider(flags))
.AddProvider("another-provider", sp => sp.GetRequiredService<SomeProvider>());
Using Provider Instances:
var provider = new InMemoryProvider(flags);
multiProviderBuilder.AddProvider("provider-name", provider);
Using Generic Type Resolution:
// Provider will be resolved from DI container
multiProviderBuilder.AddProvider<YourProvider>("provider-name");
// Or with custom factory
multiProviderBuilder.AddProvider<YourProvider>("provider-name", sp => new YourProvider(config));
Configuring Evaluation Strategy
Specify an evaluation strategy using any of these methods:
// Using generic type
multiProviderBuilder.UseStrategy<FirstMatchStrategy>();
// Using factory method with DI
multiProviderBuilder.UseStrategy(sp => new FirstMatchStrategy());
// Using strategy instance
multiProviderBuilder.UseStrategy(new ComparisonStrategy());
Using with Named Domains
Configure the MultiProvider for a specific domain:
featureBuilder
.AddMultiProvider("production-domain", multiProviderBuilder =>
{
multiProviderBuilder
.AddProvider("remote", sp => new RemoteProvider())
.AddProvider("cache", sp => new CacheProvider())
.UseStrategy<FirstSuccessfulStrategy>();
});
Basic Setup
For scenarios where dependency injection is not available, you can use the traditional setup:
using OpenFeature;
using OpenFeature.Providers.MultiProvider;
// Create your individual providers
var primaryProvider = new YourPrimaryProvider();
var fallbackProvider = new YourFallbackProvider();
// Create provider entries
var providerEntries = new[]
{
new ProviderEntry(primaryProvider, "primary"),
new ProviderEntry(fallbackProvider, "fallback")
};
// Create and set the MultiProvider
var multiProvider = new MultiProvider(providerEntries);
await Api.Instance.SetProviderAsync(multiProvider);
// Use the client as normal
var client = Api.Instance.GetClient();
var result = await client.GetBooleanValueAsync("my-flag", false);
Evaluation Strategies
The MultiProvider supports several evaluation strategies to determine how providers are evaluated:
1. FirstMatchStrategy (Default)
Returns the first result that does not indicate "flag not found". Providers are evaluated sequentially in the order they were configured.
using OpenFeature.Providers.MultiProvider.Strategies;
var strategy = new FirstMatchStrategy();
var multiProvider = new MultiProvider(providerEntries, strategy);
2. FirstSuccessfulStrategy
Returns the first result that does not result in an error. If any provider returns an error, it's ignored as long as there is a successful result.
using OpenFeature.Providers.MultiProvider.Strategies;
var strategy = new FirstSuccessfulStrategy();
var multiProvider = new MultiProvider(providerEntries, strategy);
3. ComparisonStrategy
Evaluates all providers and compares their results. Useful for testing or validation scenarios where you want to ensure providers return consistent values.
using OpenFeature.Providers.MultiProvider.Strategies;
var strategy = new ComparisonStrategy();
var multiProvider = new MultiProvider(providerEntries, strategy);
Advanced Configuration
Named Providers
You can assign names to providers for better identification and debugging:
var providerEntries = new[]
{
new ProviderEntry(new ProviderA(), "provider-a"),
new ProviderEntry(new ProviderB(), "provider-b"),
new ProviderEntry(new ProviderC(), "provider-c")
};
Custom Evaluation Context
The MultiProvider respects evaluation context and passes it to underlying providers:
var context = EvaluationContext.Builder()
.Set("userId", "user123")
.Set("environment", "production")
.Build();
var result = await client.GetBooleanValueAsync("feature-flag", false, context);
Use Cases
Primary/Fallback Configuration
Use multiple providers with fallback capabilities:
var providerEntries = new[]
{
new ProviderEntry(new RemoteProvider(), "remote"),
new ProviderEntry(new LocalCacheProvider(), "cache"),
new ProviderEntry(new StaticProvider(), "static")
};
var multiProvider = new MultiProvider(providerEntries, new FirstSuccessfulStrategy());
A/B Testing Provider Comparison
Compare results from different providers for testing purposes:
var providerEntries = new[]
{
new ProviderEntry(new ProviderA(), "provider-a"),
new ProviderEntry(new ProviderB(), "provider-b")
};
var multiProvider = new MultiProvider(providerEntries, new ComparisonStrategy());
Migration Scenarios
Gradually migrate from one provider to another:
var providerEntries = new[]
{
new ProviderEntry(new NewProvider(), "new-provider"),
new ProviderEntry(new LegacyProvider(), "legacy-provider")
};
var multiProvider = new MultiProvider(providerEntries, new FirstMatchStrategy());
Error Handling
The MultiProvider handles errors from underlying providers according to the chosen evaluation strategy:
- FirstMatchStrategy: Throws errors immediately when encountered
- FirstSuccessfulStrategy: Ignores errors if there's a successful result, throws all errors if all providers fail
- ComparisonStrategy: Collects and reports all errors for analysis
Thread Safety
The MultiProvider is thread-safe and can be used concurrently across multiple threads. It properly handles initialization and shutdown of underlying providers.
Lifecycle Management
The MultiProvider manages the lifecycle of all registered providers:
// Initialize all providers
await multiProvider.InitializeAsync(context);
// Shutdown all providers
await multiProvider.ShutdownAsync();
// Dispose (implements IAsyncDisposable)
await multiProvider.DisposeAsync();
Events
The MultiProvider supports OpenFeature events and provides specification-compliant event handling. It follows the OpenFeature Multi-Provider specification for event handling behavior.
Event Handling Example
using OpenFeature;
using OpenFeature.Providers.MultiProvider;
// Create the MultiProvider with multiple providers
var providerEntries = new[]
{
new ProviderEntry(new ProviderA(), "provider-a"),
new ProviderEntry(new ProviderB(), "provider-b")
};
var multiProvider = new MultiProvider(providerEntries);
// Subscribe to MultiProvider events
Api.Instance.AddHandler(ProviderEventTypes.ProviderReady, (eventDetails) =>
{
Console.WriteLine($"MultiProvider is ready: {eventDetails?.ProviderName}");
});
Api.Instance.AddHandler(ProviderEventTypes.ProviderStale, (eventDetails) =>
{
Console.WriteLine($"MultiProvider became stale: {eventDetails?.Message}");
});
Api.Instance.AddHandler(ProviderEventTypes.ProviderConfigurationChanged, (eventDetails) =>
{
Console.WriteLine($"Configuration changed - Flags: {string.Join(", ", eventDetails?.FlagsChanged ?? [])}");
});
Api.Instance.AddHandler(ProviderEventTypes.ProviderError, (eventDetails) =>
{
Console.WriteLine($"MultiProvider error: {eventDetails?.Message}");
});
// Set the provider - this will initialize all underlying providers
// and emit PROVIDER_READY when all are successfully initialized
await Api.Instance.SetProviderAsync(multiProvider);
// Later, if an underlying provider becomes stale and changes MultiProvider status:
// Only then will a PROVIDER_STALE event be emitted from MultiProvider
Event Lifecycle
During Initialization:
- MultiProvider emits
PROVIDER_READYwhen all underlying providers initialize successfully - MultiProvider emits
PROVIDER_ERRORif any providers fail to initialize (causing aggregate status to become ERROR/FATAL)
- MultiProvider emits
Runtime Status Changes:
- Status-changing events from underlying providers are captured internally
- MultiProvider only emits events when its aggregate status changes due to these internal events
- Example: If MultiProvider is READY and one provider becomes STALE, MultiProvider emits
PROVIDER_STALE
Configuration Changes:
PROVIDER_CONFIGURATION_CHANGEDevents from underlying providers are always re-emitted
Requirements
- .NET 8+
- .NET Framework 4.6.2+
- .NET Standard 2.0+
Contributing
See the OpenFeature .NET SDK contributing guide for details on how to contribute to this project.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net5.0 was computed. net5.0-windows was computed. net6.0 was computed. net6.0-android was computed. net6.0-ios was computed. net6.0-maccatalyst was computed. net6.0-macos was computed. net6.0-tvos was computed. net6.0-windows was computed. net7.0 was computed. net7.0-android was computed. net7.0-ios was computed. net7.0-maccatalyst was computed. net7.0-macos was computed. net7.0-tvos was computed. net7.0-windows was computed. net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 is compatible. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
| .NET Core | netcoreapp2.0 was computed. netcoreapp2.1 was computed. netcoreapp2.2 was computed. netcoreapp3.0 was computed. netcoreapp3.1 was computed. |
| .NET Standard | netstandard2.0 is compatible. netstandard2.1 was computed. |
| .NET Framework | net461 was computed. net462 is compatible. net463 was computed. net47 was computed. net471 was computed. net472 was computed. net48 was computed. net481 was computed. |
| MonoAndroid | monoandroid was computed. |
| MonoMac | monomac was computed. |
| MonoTouch | monotouch was computed. |
| Tizen | tizen40 was computed. tizen60 was computed. |
| Xamarin.iOS | xamarinios was computed. |
| Xamarin.Mac | xamarinmac was computed. |
| Xamarin.TVOS | xamarintvos was computed. |
| Xamarin.WatchOS | xamarinwatchos was computed. |
-
.NETFramework 4.6.2
- OpenFeature (>= 2.11.0)
- OpenFeature.Hosting (>= 2.11.0)
- System.Collections.Immutable (>= 8.0.0)
- System.Threading.Channels (>= 8.0.0)
- System.ValueTuple (>= 4.6.1)
-
.NETStandard 2.0
- OpenFeature (>= 2.11.0)
- OpenFeature.Hosting (>= 2.11.0)
- System.Collections.Immutable (>= 8.0.0)
- System.Threading.Channels (>= 8.0.0)
-
net10.0
- OpenFeature (>= 2.11.0)
- OpenFeature.Hosting (>= 2.11.0)
-
net8.0
- OpenFeature (>= 2.11.0)
- OpenFeature.Hosting (>= 2.11.0)
-
net9.0
- OpenFeature (>= 2.11.0)
- OpenFeature.Hosting (>= 2.11.0)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.