diff --git a/src/Microsoft.FeatureManagement/FeatureFilterEvaluationContext.cs b/src/Microsoft.FeatureManagement/FeatureFilterEvaluationContext.cs index 2591e667..91589852 100644 --- a/src/Microsoft.FeatureManagement/FeatureFilterEvaluationContext.cs +++ b/src/Microsoft.FeatureManagement/FeatureFilterEvaluationContext.cs @@ -2,6 +2,7 @@ // Licensed under the MIT license. // using Microsoft.Extensions.Configuration; +using System.Threading; namespace Microsoft.FeatureManagement { @@ -25,5 +26,10 @@ public class FeatureFilterEvaluationContext /// The settings are made available for s that implement . /// public object Settings { get; set; } + + /// + /// A cancellation token that can be used to request cancellation of the feature evaluation operation. + /// + public CancellationToken CancellationToken { get; set; } } } diff --git a/src/Microsoft.FeatureManagement/FeatureManager.cs b/src/Microsoft.FeatureManagement/FeatureManager.cs index a066078f..2be9361f 100644 --- a/src/Microsoft.FeatureManagement/FeatureManager.cs +++ b/src/Microsoft.FeatureManagement/FeatureManager.cs @@ -473,6 +473,8 @@ private async ValueTask IsEnabledAsync(FeatureDefinition feature // For all enabling filters listed in the feature's state, evaluate them according to requirement type foreach (FeatureFilterConfiguration featureFilterConfiguration in featureDefinition.EnabledFor) { + cancellationToken.ThrowIfCancellationRequested(); + filterIndex++; // @@ -525,7 +527,8 @@ private async ValueTask IsEnabledAsync(FeatureDefinition feature var context = new FeatureFilterEvaluationContext() { FeatureName = featureDefinition.Name, - Parameters = featureFilterConfiguration.Parameters + Parameters = featureFilterConfiguration.Parameters, + CancellationToken = cancellationToken }; BindSettings(filter, context, filterIndex); @@ -612,6 +615,8 @@ private ValueTask AssignVariantAsync(EvaluationEvent evaluati { foreach (UserAllocation user in evaluationEvent.FeatureDefinition.Allocation.User) { + cancellationToken.ThrowIfCancellationRequested(); + if (TargetingEvaluator.IsTargeted(targetingContext.UserId, user.Users, _assignerOptions.IgnoreCase)) { if (string.IsNullOrEmpty(user.Variant)) @@ -638,6 +643,8 @@ private ValueTask AssignVariantAsync(EvaluationEvent evaluati { foreach (GroupAllocation group in evaluationEvent.FeatureDefinition.Allocation.Group) { + cancellationToken.ThrowIfCancellationRequested(); + if (TargetingEvaluator.IsTargeted(targetingContext.Groups, group.Groups, _assignerOptions.IgnoreCase)) { if (string.IsNullOrEmpty(group.Variant)) @@ -664,6 +671,8 @@ private ValueTask AssignVariantAsync(EvaluationEvent evaluati { foreach (PercentileAllocation percentile in evaluationEvent.FeatureDefinition.Allocation.Percentile) { + cancellationToken.ThrowIfCancellationRequested(); + if (TargetingEvaluator.IsTargeted( targetingContext, percentile.From, @@ -796,7 +805,7 @@ private bool IsMatchingName(Type filterType, string filterName) // // Feature filters can have namespaces in their alias // If a feature is configured to use a filter without a namespace such as 'MyFilter', then it can match 'MyOrg.MyProduct.MyFilter' or simply 'MyFilter' - // If a feature is configured to use a filter with a namespace such as 'MyOrg.MyProduct.MyFilter' then it can only match 'MyOrg.MyProduct.MyFilter' + // If a feature is configured to use a filter with a namespace such as 'MyOrg.MyProduct.MyFilter' then it can only match 'MyOrg.MyProduct.MyFilter' if (filterName.Contains('.')) { //