From 7367e974b91ab561dcb2fc8085901639f47433fe Mon Sep 17 00:00:00 2001 From: zhiyuanliang Date: Fri, 8 Mar 2024 18:35:32 +0800 Subject: [PATCH 1/8] update README --- README.md | 56 +++++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 52 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 538ac741..a8bc6e2e 100644 --- a/README.md +++ b/README.md @@ -790,7 +790,7 @@ Each variant has two properties: a name and a configuration. The name is used to A list of all possible variants is defined for each feature under the `Variants` property. -``` +``` javascript { "FeatureManagement": { @@ -822,7 +822,7 @@ A list of all possible variants is defined for each feature under the `Variants` The process of allocating a feature's variants is determined by the `Allocation` property of the feature. -``` +``` javascript "Allocation": { "DefaultWhenEnabled": "Small", "DefaultWhenDisabled": "Small", @@ -880,13 +880,15 @@ If the feature is enabled, the feature manager will check the `User`, `Group`, a Allocation logic is similar to the [Microsoft.Targeting](./README.md#MicrosoftTargeting) feature filter, but there are some parameters that are present in targeting that aren't in allocation, and vice versa. The outcomes of targeting and allocation are not related. +**Note:** To allow allocating feature variants, you need to register `ITargetingContextAccessor`. This could be done by calling `WithTargeting` method. + ### Overriding Enabled State with a Variant You can use variants to override the enabled state of a feature flag. This gives variants an opportunity to extend the evaluation of a feature flag. If a caller is checking whether a flag that has variants is enabled, the feature manager will check if the variant assigned to the current user is set up to override the result. This is done using the optional variant property `StatusOverride`. By default, this property is set to `None`, which means the variant doesn't affect whether the flag is considered enabled or disabled. Setting `StatusOverride` to `Enabled` allows the variant, when chosen, to override a flag to be enabled. Setting `StatusOverride` to `Disabled` provides the opposite functionality, therefore disabling the flag when the variant is chosen. A feature with a `Status` of `Disabled` cannot be overridden. If you are using a feature flag with binary variants, the `StatusOverride` property can be very helpful. It allows you to continue using APIs like `IsEnabledAsync` and `FeatureGateAttribute` in your application, all while benefiting from the new features that come with variants, such as percentile allocation and seed. -``` +``` javascript "Allocation": { "Percentile": [{ "Variant": "On", @@ -914,6 +916,52 @@ If you are using a feature flag with binary variants, the `StatusOverride` prope In the above example, the feature is enabled by the `AlwaysOn` filter. If the current user is in the calculated percentile range of 10 to 20, then the `On` variant is returned. Otherwise, the `Off` variant is returned and because `StatusOverride` is equal to `Disabled`, the feature will now be considered disabled. +### Variant Service + +Dependency injection can be wired up with the variant feature flag. Different implementations of a service interface can be considered as variant services. Specific variant of an interface can be retrieved from the dependency injection container based on the allocated variant of a variant feature flag. This could be done by using `IVariantServiceProvider`. + +``` C# + IVariantServiceProvider algorithmServiceProvider; + ... + + IAlgorithm forecastAlgorithm = await algorithmServiceProvider.GetServiceAsync(cancellationToken); +``` + +The `IVaraintServiceProvider` will retrieve a specific implementation of `IAlgorithm` from the dependency injection container. It can be registered by calling `WithvariantService` on the `IFeatureManagementBuilder`. + +``` C# +services.AddFeatureManagement() + .WithVariantService("ForecastAlgorithm"); +``` + +With the call above, `IAlgorithm` will be wired up with the variant feature flag `ForecastAlgorithm`. The `GetServiceAsync` method of `IVariantService` will retrieve the variant service of `IAlgorithm` which matches the name of allocated variant of the `ForecastAlgorithm` flag, from the dependency injection container. + +``` javascript +{ + "ForecastAlgorithm": { + "Variants": [ + { + "Name": "AlgorithmBeta" + } + ] + } +} +``` + +**Note:** The `WithvariantService` method will not register any implementation of `TService`. You need to register variant serices as `TService` to allow `IVariantServiceProvider` to retrieve them from the dependency injection container. + +#### Variant Service Alias Attribute + +``` C# +[VariantServiceAlias("Beta")] +public class AlgorithmBeta : IAlgorithm +{ + ... +} +``` + +The variant service provider will use the type names of variant services to match the allocated variant. If a variant service isdecorated with the `VariantServiceAliasAttribute`, the name declared in this attribute should be used in configuration to reference this variant service. + ## Telemetry When a feature flag change is deployed, it is often important to analyze its effect on an application. For example, here are a few questions that may arise: @@ -931,7 +979,7 @@ By default, feature flags will not have telemetry emitted. To publish telemetry For flags defined in `appsettings.json`, that is done by using the `Telemetry` property on feature flags. -``` +``` javascript { "FeatureManagement": { From 8db8bb4cdc8aa7931de9c35bc7a5100f4961c950 Mon Sep 17 00:00:00 2001 From: zhiyuanliang Date: Sun, 10 Mar 2024 18:19:16 +0800 Subject: [PATCH 2/8] update --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index a8bc6e2e..4fdcccc8 100644 --- a/README.md +++ b/README.md @@ -870,7 +870,7 @@ The `Allocation` setting of a feature flag has the following properties: | `DefaultWhenDisabled` | Specifies which variant should be used when a variant is requested while the feature is considered disabled. | | `DefaultWhenEnabled` | Specifies which variant should be used when a variant is requested while the feature is considered enabled and no other variant was assigned to the user. | | `User` | Specifies a variant and a list of users to whom that variant should be assigned. | -| `Group` | Specifies a variant and a list of groups the current user has to be in for that variant to be assigned. | +| `Group` | Specifies a variant and a list of groups the current user has to be in at least one of for that variant to be used. | | `Percentile` | Specifies a variant and a percentage range the user's calculated percentage has to fit into for that variant to be assigned. | | `Seed` | The value which percentage calculations for `Percentile` are based on. The percentage calculation for a specific user will be the same across all features if the same `Seed` value is used. If no `Seed` is specified, then a default seed is created based on the feature name. | From 18ba83136587f3108833cef6370a73aaaa6a12cc Mon Sep 17 00:00:00 2001 From: zhiyuanliang Date: Wed, 13 Mar 2024 15:11:49 +0800 Subject: [PATCH 3/8] update --- README.md | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index d9927e5a..e6c8c0f9 100644 --- a/README.md +++ b/README.md @@ -36,6 +36,7 @@ Here are some of the benefits of using this library: * [Targeting](#targeting) * [Targeting Exclusion](#targeting-exclusion) * [Variants](#variants) +* [Variants in Dependency Injection](#variants-in-dependency-injection) * [Telemetry](#telemetry) * [Enabling Telemetry](#enabling-telemetry) * [Custom Telemetry Publishers](#custom-telemetry-publishers) @@ -972,15 +973,15 @@ If you are using a feature flag with binary variants, the `StatusOverride` prope In the above example, the feature is enabled by the `AlwaysOn` filter. If the current user is in the calculated percentile range of 10 to 20, then the `On` variant is returned. Otherwise, the `Off` variant is returned and because `StatusOverride` is equal to `Disabled`, the feature will now be considered disabled. -### Variant Service +## Variants in Dependency Injection -Dependency injection can be wired up with the variant feature flag. Different implementations of a service interface can be considered as variant services. Specific variant of an interface can be retrieved from the dependency injection container based on the allocated variant of a variant feature flag. This could be done by using `IVariantServiceProvider`. +Dependency injection can be wired up with a variant feature flag. Different implementations of a service interface can be considered as variant services. Specific variant of an interface can be retrieved from the dependency injection container based on the allocated variant of a variant feature flag. This could be done by using `IVariantServiceProvider`. ``` C# - IVariantServiceProvider algorithmServiceProvider; - ... +IVariantServiceProvider algorithmServiceProvider; +... - IAlgorithm forecastAlgorithm = await algorithmServiceProvider.GetServiceAsync(cancellationToken); +IAlgorithm forecastAlgorithm = await algorithmServiceProvider.GetServiceAsync(cancellationToken); ``` The `IVaraintServiceProvider` will retrieve a specific implementation of `IAlgorithm` from the dependency injection container. It can be registered by calling `WithvariantService` on the `IFeatureManagementBuilder`. From 4febe1ac88214df36b08d41170a11fbdc9ea21c4 Mon Sep 17 00:00:00 2001 From: Zhiyuan Liang Date: Fri, 15 Mar 2024 11:13:25 +0800 Subject: [PATCH 4/8] update --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index e6c8c0f9..b2426b78 100644 --- a/README.md +++ b/README.md @@ -925,7 +925,7 @@ The `Allocation` setting of a feature flag has the following properties: | `DefaultWhenDisabled` | Specifies which variant should be used when a variant is requested while the feature is considered disabled. | | `DefaultWhenEnabled` | Specifies which variant should be used when a variant is requested while the feature is considered enabled and no other variant was assigned to the user. | | `User` | Specifies a variant and a list of users to whom that variant should be assigned. | -| `Group` | Specifies a variant and a list of groups the current user has to be in at least one of for that variant to be used. | +| `Group` | Specifies a variant and a list of groups. The variant will be assigned if the user is in at least one of the groups. | | `Percentile` | Specifies a variant and a percentage range the user's calculated percentage has to fit into for that variant to be assigned. | | `Seed` | The value which percentage calculations for `Percentile` are based on. The percentage calculation for a specific user will be the same across all features if the same `Seed` value is used. If no `Seed` is specified, then a default seed is created based on the feature name. | @@ -975,7 +975,7 @@ In the above example, the feature is enabled by the `AlwaysOn` filter. If the cu ## Variants in Dependency Injection -Dependency injection can be wired up with a variant feature flag. Different implementations of a service interface can be considered as variant services. Specific variant of an interface can be retrieved from the dependency injection container based on the allocated variant of a variant feature flag. This could be done by using `IVariantServiceProvider`. +Dependency injection can be wired up with a variant feature flag. You can use a variant feature flag to control which implementation of a service should be used by the dependency injection, based on the allocated variant of the feature flag. This could be done by using `IVariantServiceProvider`. ``` C# IVariantServiceProvider algorithmServiceProvider; @@ -984,7 +984,7 @@ IVariantServiceProvider algorithmServiceProvider; IAlgorithm forecastAlgorithm = await algorithmServiceProvider.GetServiceAsync(cancellationToken); ``` -The `IVaraintServiceProvider` will retrieve a specific implementation of `IAlgorithm` from the dependency injection container. It can be registered by calling `WithvariantService` on the `IFeatureManagementBuilder`. +The `IVaraintServiceProvider` will retrieve a specific implementation of `IAlgorithm` from the dependency injection container. It can be registered by calling `WithVariantService` on the `IFeatureManagementBuilder`. ``` C# services.AddFeatureManagement() From efa42dd30252dcff36ab08608765c73ba1d4c3bc Mon Sep 17 00:00:00 2001 From: Zhiyuan Liang Date: Fri, 15 Mar 2024 11:30:12 +0800 Subject: [PATCH 5/8] update --- README.md | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index b2426b78..575fc402 100644 --- a/README.md +++ b/README.md @@ -996,16 +996,21 @@ With the call above, `IAlgorithm` will be wired up with the variant feature flag ``` javascript { "ForecastAlgorithm": { - "Variants": [ + "Variants": [ { "Name": "AlgorithmBeta" - } + }, + ... ] } } ``` -**Note:** The `WithvariantService` method will not register any implementation of `TService`. You need to register variant serices as `TService` to allow `IVariantServiceProvider` to retrieve them from the dependency injection container. +**Note:** The `WithvariantService` method will not register any implementation of `TService`. You need to register variant serices as `TService` to allow `IVariantServiceProvider` to retrieve them from the dependency injection container. Which extension method to use for registration depends on your needs. Here is an example. + +``` C# +services.AddSingleton(); +``` #### Variant Service Alias Attribute From 80d2817bf21398416f0c186ac40ff050fdf66fdf Mon Sep 17 00:00:00 2001 From: Zhiyuan Liang Date: Fri, 15 Mar 2024 11:32:56 +0800 Subject: [PATCH 6/8] fix typo --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 575fc402..b254f9f2 100644 --- a/README.md +++ b/README.md @@ -1022,7 +1022,7 @@ public class AlgorithmBeta : IAlgorithm } ``` -The variant service provider will use the type names of variant services to match the allocated variant. If a variant service isdecorated with the `VariantServiceAliasAttribute`, the name declared in this attribute should be used in configuration to reference this variant service. +The variant service provider will use the type names of implementations to match the allocated variant. If a variant service is decorated with the `VariantServiceAliasAttribute`, the name declared in this attribute should be used in configuration to reference this variant service. ## Telemetry From c5fb3d7d4cbf609767b66021fdcbb74107ddd631 Mon Sep 17 00:00:00 2001 From: zhiyuanliang Date: Tue, 19 Mar 2024 16:08:32 +0800 Subject: [PATCH 7/8] update --- README.md | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index b254f9f2..fba43fc6 100644 --- a/README.md +++ b/README.md @@ -36,7 +36,7 @@ Here are some of the benefits of using this library: * [Targeting](#targeting) * [Targeting Exclusion](#targeting-exclusion) * [Variants](#variants) -* [Variants in Dependency Injection](#variants-in-dependency-injection) + * [Variants in Dependency Injection](#variants-in-dependency-injection) * [Telemetry](#telemetry) * [Enabling Telemetry](#enabling-telemetry) * [Custom Telemetry Publishers](#custom-telemetry-publishers) @@ -935,7 +935,7 @@ If the feature is enabled, the feature manager will check the `User`, `Group`, a Allocation logic is similar to the [Microsoft.Targeting](./README.md#MicrosoftTargeting) feature filter, but there are some parameters that are present in targeting that aren't in allocation, and vice versa. The outcomes of targeting and allocation are not related. -**Note:** To allow allocating feature variants, you need to register `ITargetingContextAccessor`. This could be done by calling `WithTargeting` method. +**Note:** To allow allocating feature variants, you need to register `ITargetingContextAccessor`. This can be done by calling the `WithTargeting` method. ### Overriding Enabled State with a Variant @@ -973,9 +973,9 @@ If you are using a feature flag with binary variants, the `StatusOverride` prope In the above example, the feature is enabled by the `AlwaysOn` filter. If the current user is in the calculated percentile range of 10 to 20, then the `On` variant is returned. Otherwise, the `Off` variant is returned and because `StatusOverride` is equal to `Disabled`, the feature will now be considered disabled. -## Variants in Dependency Injection +### Variants in Dependency Injection -Dependency injection can be wired up with a variant feature flag. You can use a variant feature flag to control which implementation of a service should be used by the dependency injection, based on the allocated variant of the feature flag. This could be done by using `IVariantServiceProvider`. +Variant feature flags can be used in conjunction with dependency injection to surface different implementations of a service for different users. This is accomplished through the use of the `IVariantServiceProvider` interface. ``` C# IVariantServiceProvider algorithmServiceProvider; @@ -984,17 +984,22 @@ IVariantServiceProvider algorithmServiceProvider; IAlgorithm forecastAlgorithm = await algorithmServiceProvider.GetServiceAsync(cancellationToken); ``` -The `IVaraintServiceProvider` will retrieve a specific implementation of `IAlgorithm` from the dependency injection container. It can be registered by calling `WithVariantService` on the `IFeatureManagementBuilder`. +In the snippet above, the `IVariantServiceProvider` will retrieve an implementation of `IAlgorithm` from the dependency injection container. The chosen implementation is dependent upon: +* The feature that the `IAlgorithm` service was registered with. +* The allocated variant for that feature. + +The `IVariantServiceProvider` is made available to the application by calling `IFeatureManagementBuilder.WithVariantService(string featureName)`. See below for an example. ``` C# services.AddFeatureManagement() .WithVariantService("ForecastAlgorithm"); ``` -With the call above, `IAlgorithm` will be wired up with the variant feature flag `ForecastAlgorithm`. The `GetServiceAsync` method of `IVariantService` will retrieve the variant service of `IAlgorithm` which matches the name of allocated variant of the `ForecastAlgorithm` flag, from the dependency injection container. +With the call above, `IAlgorithm` will be wired up with the variant feature flag `ForecastAlgorithm`. The `GetServiceAsync` method of `IVariantService` will retrieve the variant service of `IAlgorithm` which matches the name of allocated variant of the `ForecastAlgorithm` flag, from the dependency injection container. If there is no appropriate variant service found, the `GetServiceAsync` method will return null. ``` javascript { + // The example variant feature flag "ForecastAlgorithm": { "Variants": [ { @@ -1006,9 +1011,10 @@ With the call above, `IAlgorithm` will be wired up with the variant feature flag } ``` -**Note:** The `WithvariantService` method will not register any implementation of `TService`. You need to register variant serices as `TService` to allow `IVariantServiceProvider` to retrieve them from the dependency injection container. Which extension method to use for registration depends on your needs. Here is an example. +**Note:** The `WithVariantService` method will not register any implementation(s) of `TService`. You need to register variant services as `TService` to allow `IVariantServiceProvider` to retrieve them from the dependency injection container. Which extension method to use for registration depends on your needs. Here is an example. ``` C# +// Adds an implementation for `IAlgorithm`, which is consumed as a variant service above. services.AddSingleton(); ``` From a902b985bfa3ecfd8283ba74d1c279b6373f8312 Mon Sep 17 00:00:00 2001 From: Zhiyuan Liang Date: Thu, 21 Mar 2024 11:02:46 +0800 Subject: [PATCH 8/8] update --- README.md | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index fba43fc6..3c3703b3 100644 --- a/README.md +++ b/README.md @@ -985,7 +985,7 @@ IAlgorithm forecastAlgorithm = await algorithmServiceProvider.GetServiceAsync(ca ``` In the snippet above, the `IVariantServiceProvider` will retrieve an implementation of `IAlgorithm` from the dependency injection container. The chosen implementation is dependent upon: -* The feature that the `IAlgorithm` service was registered with. +* The feature flag that the `IAlgorithm` service was registered with. * The allocated variant for that feature. The `IVariantServiceProvider` is made available to the application by calling `IFeatureManagementBuilder.WithVariantService(string featureName)`. See below for an example. @@ -995,7 +995,7 @@ services.AddFeatureManagement() .WithVariantService("ForecastAlgorithm"); ``` -With the call above, `IAlgorithm` will be wired up with the variant feature flag `ForecastAlgorithm`. The `GetServiceAsync` method of `IVariantService` will retrieve the variant service of `IAlgorithm` which matches the name of allocated variant of the `ForecastAlgorithm` flag, from the dependency injection container. If there is no appropriate variant service found, the `GetServiceAsync` method will return null. +The call above makes `IVariantServiceProvider` available in the service collection. Implementation(s) of `IAlgorithm` must be added separately via an add method such as `services.AddSingleton()`. The implementation of `IAlgorithm` that the `IVariantServiceProvider` uses depends on the `ForecastAlgorithm` variant feature flag. If no implementation of `IAlgorithm` is added to the service collection, then the `IVariantServiceProvider.GetServiceAsync()` will return a task with a *null* result. ``` javascript { @@ -1011,13 +1011,6 @@ With the call above, `IAlgorithm` will be wired up with the variant feature flag } ``` -**Note:** The `WithVariantService` method will not register any implementation(s) of `TService`. You need to register variant services as `TService` to allow `IVariantServiceProvider` to retrieve them from the dependency injection container. Which extension method to use for registration depends on your needs. Here is an example. - -``` C# -// Adds an implementation for `IAlgorithm`, which is consumed as a variant service above. -services.AddSingleton(); -``` - #### Variant Service Alias Attribute ``` C#