diff --git a/src/Microsoft.FeatureManagement/FeatureManager.cs b/src/Microsoft.FeatureManagement/FeatureManager.cs index 00ff95a3..3cb35c7a 100644 --- a/src/Microsoft.FeatureManagement/FeatureManager.cs +++ b/src/Microsoft.FeatureManagement/FeatureManager.cs @@ -355,14 +355,6 @@ private async ValueTask EvaluateFeature(string featur } } - if (_sessionManagers != null) - { - foreach (ISessionManager sessionManager in _sessionManagers) - { - await sessionManager.SetAsync(evaluationEvent.FeatureDefinition.Name, evaluationEvent.Enabled).ConfigureAwait(false); - } - } - // Only add an activity event if telemetry is enabled for the feature and the activity is valid if (telemetryEnabled && Activity.Current != null && @@ -371,6 +363,28 @@ private async ValueTask EvaluateFeature(string featur FeatureEvaluationTelemetry.Publish(evaluationEvent, Logger); } } + else if (_sessionManagers != null) + { + foreach (ISessionManager sessionManager in _sessionManagers) + { + bool? readSessionResult = await sessionManager.GetAsync(feature).ConfigureAwait(false); + + if (readSessionResult.HasValue) + { + evaluationEvent.Enabled = readSessionResult.Value; + + break; + } + } + } + + if (_sessionManagers != null) + { + foreach (ISessionManager sessionManager in _sessionManagers) + { + await sessionManager.SetAsync(feature, evaluationEvent.Enabled).ConfigureAwait(false); + } + } return evaluationEvent; } diff --git a/tests/Tests.FeatureManagement/FeatureManagementTest.cs b/tests/Tests.FeatureManagement/FeatureManagementTest.cs index 10636b84..12178672 100644 --- a/tests/Tests.FeatureManagement/FeatureManagementTest.cs +++ b/tests/Tests.FeatureManagement/FeatureManagementTest.cs @@ -280,6 +280,61 @@ public async Task ThrowsForMissingFeatures() featureManager.IsEnabledAsync("NonExistentFeature")); } + [Fact] + public async Task SessionManagerQueriedWhenFeatureDefinitionIsNull() + { + IConfiguration config = new ConfigurationBuilder().AddJsonFile("appsettings.json").Build(); + + var services = new ServiceCollection(); + + ISessionManager sessionManager = new TestSessionManager(); + + await sessionManager.SetAsync("UnexistedFeature", true); + + services + .AddSingleton(config) + .AddSingleton(sessionManager) + .AddFeatureManagement(); + + ServiceProvider serviceProvider = services.BuildServiceProvider(); + + IFeatureManager featureManager = serviceProvider.GetRequiredService(); + + // Feature doesn't exist in configuration, but should return true from session + Assert.True(await featureManager.IsEnabledAsync("UnexistedFeature")); + + // Set the feature to false in session + await sessionManager.SetAsync("UnexistedFeature", false); + + // Should return false from session + Assert.False(await featureManager.IsEnabledAsync("UnexistedFeature")); + } + + [Fact] + public async Task SetResultInSessionManagerWhenFeatureDefinitionIsNull() + { + IConfiguration config = new ConfigurationBuilder().AddJsonFile("appsettings.json").Build(); + + var services = new ServiceCollection(); + + ISessionManager sessionManager = new TestSessionManager(); + + services + .AddSingleton(config) + .AddSingleton(sessionManager) + .AddFeatureManagement(); + + ServiceProvider serviceProvider = services.BuildServiceProvider(); + + IFeatureManager featureManager = serviceProvider.GetRequiredService(); + + // session manager is clean here + var result = await featureManager.IsEnabledAsync("UnexistedFeature"); + + // session manager should store the result after evaluation + Assert.Equal(result, await sessionManager.GetAsync("UnexistedFeature")); + } + [Fact] public async Task ThreadSafeSnapshot() { diff --git a/tests/Tests.FeatureManagement/TestSessionManager.cs b/tests/Tests.FeatureManagement/TestSessionManager.cs new file mode 100644 index 00000000..37e0f635 --- /dev/null +++ b/tests/Tests.FeatureManagement/TestSessionManager.cs @@ -0,0 +1,31 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. +// +using Microsoft.FeatureManagement; +using System.Collections.Concurrent; +using System.Threading.Tasks; + +namespace Tests.FeatureManagement +{ + class TestSessionManager : ISessionManager + { + private readonly ConcurrentDictionary _session = new ConcurrentDictionary(); + + public Task SetAsync(string featureName, bool enabled) + { + _session[featureName] = enabled; + + return Task.CompletedTask; + } + + public Task GetAsync(string featureName) + { + if (_session.TryGetValue(featureName, out bool enabled)) + { + return Task.FromResult(enabled); + } + + return Task.FromResult(null); + } + } +}