Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,12 @@ public class FeatureFilterEvaluationContext
public IConfiguration Parameters { get; set; }

/// <summary>
/// The settings provided for the feature filter to use when evaluating whether the feature should be enabled. This property takes precedence over <see cref="Settings"/> and <see cref="Parameters"/> if both are provided.
/// </summary>
public object ParametersObject { get; set; }

/// <summary>
/// A settings object, if any, that has been pre-bound from <see cref="Parameters"/>.
/// The settings are made available for <see cref="IFeatureFilter"/>s that implement <see cref="IFilterParametersBinder"/>.
/// A settings object, if any, provided for the feature filter to use when evaluating whether the feature should be enabled.
/// This property is populated in two cases:
/// <list type="bullet">
/// <item>For features that provide parameters as an object, via <see cref="FeatureFilterConfiguration.ParametersObject"/>.</item>
/// <item>For <see cref="IFeatureFilter"/>s that implement <see cref="IFilterParametersBinder"/>.</item>
/// </list>
/// </summary>
public object Settings { get; set; }

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,18 +49,16 @@ public Task<bool> EvaluateAsync(FeatureFilterEvaluationContext context)
throw new ArgumentNullException(nameof(context));
}

//
// Check if ParametersObject available (takes precedence), then prebound settings, otherwise bind from parameters.
PercentageFilterSettings settings;

if (context.ParametersObject != null && !(context.ParametersObject is PercentageFilterSettings))
if (context.Settings != null && !(context.Settings is PercentageFilterSettings))
{
throw new ArgumentException(
$"The '{Alias}' feature filter for feature '{context.FeatureName}' has a {nameof(context.ParametersObject)} of type '{context.ParametersObject.GetType()}', but expected '{typeof(PercentageFilterSettings)}'.",
nameof(context.ParametersObject));
$"The '{Alias}' feature filter for feature '{context.FeatureName}' has a {nameof(context.Settings)} value of type '{context.Settings.GetType()}', but expected '{typeof(PercentageFilterSettings)}'.",
nameof(context.Settings));
}

settings = (PercentageFilterSettings)context.ParametersObject ?? (PercentageFilterSettings)context.Settings ?? (PercentageFilterSettings)BindParameters(context.Parameters);
//
// Check if prebound settings available, otherwise bind from parameters.
PercentageFilterSettings settings = (PercentageFilterSettings)context.Settings ?? (PercentageFilterSettings)BindParameters(context.Parameters);

bool result = true;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,18 +70,16 @@ public Task<bool> EvaluateAsync(FeatureFilterEvaluationContext context)
throw new ArgumentNullException(nameof(context));
}

//
// Check if ParametersObject available (takes precedence), then prebound settings, otherwise bind from parameters.
TimeWindowFilterSettings settings;

if (context.ParametersObject != null && !(context.ParametersObject is TimeWindowFilterSettings))
if (context.Settings != null && !(context.Settings is TimeWindowFilterSettings))
{
throw new ArgumentException(
$"The '{Alias}' feature filter for feature '{context.FeatureName}' has a {nameof(context.ParametersObject)} of type '{context.ParametersObject.GetType()}', but expected '{typeof(TimeWindowFilterSettings)}'.",
nameof(context.ParametersObject));
$"The '{Alias}' feature filter for feature '{context.FeatureName}' has a {nameof(context.Settings)} value of type '{context.Settings.GetType()}', but expected '{typeof(TimeWindowFilterSettings)}'.",
nameof(context.Settings));
}

settings = (TimeWindowFilterSettings)context.ParametersObject ?? (TimeWindowFilterSettings)context.Settings ?? (TimeWindowFilterSettings)BindParameters(context.Parameters);
//
// Check if prebound settings available, otherwise bind from parameters.
TimeWindowFilterSettings settings = (TimeWindowFilterSettings)context.Settings ?? (TimeWindowFilterSettings)BindParameters(context.Parameters);

DateTimeOffset now = SystemClock?.GetUtcNow() ?? DateTimeOffset.UtcNow;

Expand Down
7 changes: 5 additions & 2 deletions src/Microsoft.FeatureManagement/FeatureManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -498,11 +498,14 @@ private async ValueTask<bool> IsEnabledAsync<TContext>(FeatureDefinition feature
{
FeatureName = featureDefinition.Name,
Parameters = featureFilterConfiguration.Parameters,
ParametersObject = featureFilterConfiguration.ParametersObject,
Settings = featureFilterConfiguration.ParametersObject,
CancellationToken = cancellationToken
};

BindSettings(filter, context, filterIndex);
if (context.Settings == null)
{
BindSettings(filter, context, filterIndex);
}

//
// IContextualFeatureFilter
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,18 +67,16 @@ public Task<bool> EvaluateAsync(FeatureFilterEvaluationContext context, ITargeti
throw new ArgumentNullException(nameof(targetingContext));
}

//
// Check if ParametersObject available (takes precedence), then prebound settings, otherwise bind from parameters.
TargetingFilterSettings settings;

if (context.ParametersObject != null && !(context.ParametersObject is TargetingFilterSettings))
if (context.Settings != null && !(context.Settings is TargetingFilterSettings))
{
throw new ArgumentException(
$"The '{Alias}' feature filter for feature '{context.FeatureName}' has a {nameof(context.ParametersObject)} of type '{context.ParametersObject.GetType()}', but expected '{typeof(TargetingFilterSettings)}'.",
nameof(context.ParametersObject));
$"The '{Alias}' feature filter for feature '{context.FeatureName}' has a {nameof(context.Settings)} value of type '{context.Settings.GetType()}', but expected '{typeof(TargetingFilterSettings)}'.",
nameof(context.Settings));
}

settings = (TargetingFilterSettings)context.ParametersObject ?? (TargetingFilterSettings)context.Settings ?? (TargetingFilterSettings)BindParameters(context.Parameters);
//
// Check if prebound settings available, otherwise bind from parameters.
TargetingFilterSettings settings = (TargetingFilterSettings)context.Settings ?? (TargetingFilterSettings)BindParameters(context.Parameters);

return Task.FromResult(TargetingEvaluator.IsTargeted(targetingContext, settings, _options.IgnoreCase, context.FeatureName));
}
Expand Down
7 changes: 3 additions & 4 deletions tests/Tests.FeatureManagement/FeatureManagementTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1094,9 +1094,9 @@ public async Task UsesParametersObject()
testFeatureFilter.Callback = (evaluationContext) =>
{
//
// When ParametersObject is set, it should be available on the context
// so custom filters can use it with their own precedence logic.
Assert.Same(parameterObject, evaluationContext.ParametersObject);
// When ParametersObject is set, it should be available on the context via settings
// so custom filters can use it.
Assert.Same(parameterObject, evaluationContext.Settings);

return Task.FromResult(true);
};
Expand Down Expand Up @@ -1157,7 +1157,6 @@ public async Task ParametersObjectFallsBackToParametersWhenNull()
//
// When ParametersObject is null, Settings should be populated
// by IFilterParametersBinder as usual.
Assert.Null(evaluationContext.ParametersObject);
Assert.NotNull(evaluationContext.Settings);

return Task.FromResult(true);
Expand Down
Loading