Skip to content

Commit 99b1bef

Browse files
authored
Merge pull request #514 from microsoft/main
Merge main into release/v4 for 4.0.0 release
2 parents e37277a + 2c94bdf commit 99b1bef

17 files changed

+445
-255
lines changed

.github/pull_request_template.md

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
## Why this PR?
2+
3+
-motivation for making this change-
4+
5+
## Visible Changes
6+
7+
-changes that are visible to developers using this library-

build/install-dotnet.ps1

+3-1
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,6 @@
77

88
&([scriptblock]::Create((Invoke-WebRequest -UseBasicParsing 'https://dot.net/v1/dotnet-install.ps1'))) -Channel 7.0
99

10-
&([scriptblock]::Create((Invoke-WebRequest -UseBasicParsing 'https://dot.net/v1/dotnet-install.ps1'))) -Channel 8.0
10+
&([scriptblock]::Create((Invoke-WebRequest -UseBasicParsing 'https://dot.net/v1/dotnet-install.ps1'))) -Channel 8.0
11+
12+
&([scriptblock]::Create((Invoke-WebRequest -UseBasicParsing 'https://dot.net/v1/dotnet-install.ps1'))) -Channel 9.0

examples/ConsoleApp/ConsoleApp.csproj

+3-3
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,9 @@
77
</PropertyGroup>
88

99
<ItemGroup>
10-
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="8.0.0" />
11-
<PackageReference Include="System.Text.Json" Version="8.0.4" />
12-
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="8.0.0" />
10+
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="8.0.1" />
11+
<PackageReference Include="System.Text.Json" Version="8.0.5" />
12+
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="8.0.1" />
1313
</ItemGroup>
1414

1515
<ItemGroup>

examples/ConsoleApp/appsettings.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -17,4 +17,4 @@
1717
}
1818
]
1919
}
20-
}
20+
}

examples/TargetingConsoleApp/TargetingConsoleApp.csproj

+3-3
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,9 @@
77
</PropertyGroup>
88

99
<ItemGroup>
10-
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="8.0.0" />
11-
<PackageReference Include="System.Text.Json" Version="8.0.4" />
12-
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="8.0.0" />
10+
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="8.0.1" />
11+
<PackageReference Include="System.Text.Json" Version="8.0.5" />
12+
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="8.0.1" />
1313
</ItemGroup>
1414

1515
<ItemGroup>

src/Microsoft.FeatureManagement.AspNetCore/FeatureGateAttribute.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ public FeatureGateAttribute(RequirementType requirementType, params object[] fea
9595
public RequirementType RequirementType { get; }
9696

9797
/// <summary>
98-
/// Performs controller action pre-procesing to ensure that any or all of the specified features are enabled.
98+
/// Performs controller action pre-processing to ensure that any or all of the specified features are enabled.
9999
/// </summary>
100100
/// <param name="context">The context of the MVC action.</param>
101101
/// <param name="next">The action delegate.</param>

src/Microsoft.FeatureManagement.AspNetCore/Microsoft.FeatureManagement.AspNetCore.csproj

+1-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
<Import Project="..\..\build\Versioning.props" />
1313

1414
<PropertyGroup>
15-
<TargetFrameworks>net6.0;net7.0;net8.0</TargetFrameworks>
15+
<TargetFrameworks>net6.0;net8.0;net9.0</TargetFrameworks>
1616
<SignAssembly>true</SignAssembly>
1717
<DelaySign>false</DelaySign>
1818
<AssemblyOriginatorKeyFile>..\..\build\Microsoft.FeatureManagement.snk</AssemblyOriginatorKeyFile>

src/Microsoft.FeatureManagement.AspNetCore/TagHelpers/FeatureTagHelper.cs

+7-3
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,8 @@ namespace Microsoft.FeatureManagement.Mvc.TagHelpers
1414
/// </summary>
1515
public class FeatureTagHelper : TagHelper
1616
{
17-
private readonly IVariantFeatureManager _featureManager;
17+
private readonly IFeatureManager _featureManager;
18+
private readonly IVariantFeatureManager _variantFeatureManager;
1819

1920
/// <summary>
2021
/// A feature name, or comma separated list of feature names, for which the content should be rendered. By default, all specified features must be enabled to render the content, but this requirement can be controlled by the <see cref="Requirement"/> property.
@@ -41,9 +42,12 @@ public class FeatureTagHelper : TagHelper
4142
/// Creates a feature tag helper.
4243
/// </summary>
4344
/// <param name="featureManager">The feature manager snapshot to use to evaluate feature state.</param>
44-
public FeatureTagHelper(IVariantFeatureManagerSnapshot featureManager)
45+
/// <param name="variantFeatureManager">The variant feature manager snapshot to use to evaluate feature state.</param>
46+
public FeatureTagHelper(IFeatureManagerSnapshot featureManager, IVariantFeatureManagerSnapshot variantFeatureManager)
4547
{
48+
// Takes both a feature manager and a variant feature manager for backwards compatibility.
4649
_featureManager = featureManager ?? throw new ArgumentNullException(nameof(featureManager));
50+
_variantFeatureManager = variantFeatureManager ?? throw new ArgumentNullException(nameof(variantFeatureManager));
4751
}
4852

4953
/// <summary>
@@ -84,7 +88,7 @@ public override async Task ProcessAsync(TagHelperContext context, TagHelperOutpu
8488
enabled = await variants.Any(
8589
async variant =>
8690
{
87-
Variant assignedVariant = await _featureManager.GetVariantAsync(features.First()).ConfigureAwait(false);
91+
Variant assignedVariant = await _variantFeatureManager.GetVariantAsync(features.First()).ConfigureAwait(false);
8892

8993
return variant == assignedVariant?.Name;
9094
});

src/Microsoft.FeatureManagement/ConfigurationFeatureDefinitionProvider.cs

+21-18
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,10 @@ public sealed class ConfigurationFeatureDefinitionProvider : IFeatureDefinitionP
2424
// provider to be marked for caching as well.
2525

2626
private readonly IConfiguration _configuration;
27-
private readonly ConcurrentDictionary<string, FeatureDefinition> _definitions;
27+
private readonly ConcurrentDictionary<string, Task<FeatureDefinition>> _definitions;
2828
private IDisposable _changeSubscription;
2929
private int _stale = 0;
30+
private readonly Func<string, Task<FeatureDefinition>> _getFeatureDefinitionFunc;
3031

3132
const string ParseValueErrorString = "Invalid setting '{0}' with value '{1}' for feature '{2}'.";
3233

@@ -37,11 +38,16 @@ public sealed class ConfigurationFeatureDefinitionProvider : IFeatureDefinitionP
3738
public ConfigurationFeatureDefinitionProvider(IConfiguration configuration)
3839
{
3940
_configuration = configuration ?? throw new ArgumentNullException(nameof(configuration));
40-
_definitions = new ConcurrentDictionary<string, FeatureDefinition>();
41+
_definitions = new ConcurrentDictionary<string, Task<FeatureDefinition>>();
4142

4243
_changeSubscription = ChangeToken.OnChange(
4344
() => _configuration.GetReloadToken(),
4445
() => _stale = 1);
46+
47+
_getFeatureDefinitionFunc = (featureName) =>
48+
{
49+
return Task.FromResult(GetMicrosoftSchemaFeatureDefinition(featureName) ?? GetDotnetSchemaFeatureDefinition(featureName));
50+
};
4551
}
4652

4753
/// <summary>
@@ -86,10 +92,7 @@ public Task<FeatureDefinition> GetFeatureDefinitionAsync(string featureName)
8692
_definitions.Clear();
8793
}
8894

89-
return Task.FromResult(
90-
_definitions.GetOrAdd(
91-
featureName,
92-
(_) => GetMicrosoftSchemaFeatureDefinition(featureName) ?? GetDotnetSchemaFeatureDefinition(featureName)));
95+
return _definitions.GetOrAdd(featureName, _getFeatureDefinitionFunc);
9396
}
9497

9598
/// <summary>
@@ -98,7 +101,7 @@ public Task<FeatureDefinition> GetFeatureDefinitionAsync(string featureName)
98101
/// <returns>An enumerator which provides asynchronous iteration over feature definitions.</returns>
99102
//
100103
// The async key word is necessary for creating IAsyncEnumerable.
101-
// The need to disable this warning occurs when implementaing async stream synchronously.
104+
// The need to disable this warning occurs when implementing async stream synchronously.
102105
#pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously
103106
public async IAsyncEnumerable<FeatureDefinition> GetAllFeatureDefinitionsAsync()
104107
#pragma warning restore CS1998
@@ -121,7 +124,7 @@ public async IAsyncEnumerable<FeatureDefinition> GetAllFeatureDefinitionsAsync()
121124

122125
//
123126
// Underlying IConfigurationSection data is dynamic so latest feature definitions are returned
124-
FeatureDefinition definition = _definitions.GetOrAdd(featureName, (_) => ParseMicrosoftSchemaFeatureDefinition(featureSection));
127+
FeatureDefinition definition = _definitions.GetOrAdd(featureName, _getFeatureDefinitionFunc).Result;
125128

126129
if (definition != null)
127130
{
@@ -142,7 +145,7 @@ public async IAsyncEnumerable<FeatureDefinition> GetAllFeatureDefinitionsAsync()
142145

143146
//
144147
// Underlying IConfigurationSection data is dynamic so latest feature definitions are returned
145-
FeatureDefinition definition = _definitions.GetOrAdd(featureName, (_) => ParseDotnetSchemaFeatureDefinition(featureSection));
148+
FeatureDefinition definition = _definitions.GetOrAdd(featureName, _getFeatureDefinitionFunc).Result;
146149

147150
if (definition != null)
148151
{
@@ -155,32 +158,32 @@ private FeatureDefinition GetDotnetSchemaFeatureDefinition(string featureName)
155158
{
156159
IEnumerable<IConfigurationSection> dotnetFeatureDefinitionSections = GetDotnetFeatureDefinitionSections();
157160

158-
IConfigurationSection configuration = dotnetFeatureDefinitionSections
161+
IConfigurationSection dotnetFeatureDefinitionConfiguration = dotnetFeatureDefinitionSections
159162
.FirstOrDefault(section =>
160163
string.Equals(section.Key, featureName, StringComparison.OrdinalIgnoreCase));
161164

162-
if (configuration == null)
165+
if (dotnetFeatureDefinitionConfiguration == null)
163166
{
164167
return null;
165168
}
166169

167-
return ParseDotnetSchemaFeatureDefinition(configuration);
170+
return ParseDotnetSchemaFeatureDefinition(dotnetFeatureDefinitionConfiguration);
168171
}
169172

170173
private FeatureDefinition GetMicrosoftSchemaFeatureDefinition(string featureName)
171174
{
172175
IEnumerable<IConfigurationSection> microsoftFeatureDefinitionSections = GetMicrosoftFeatureDefinitionSections();
173176

174-
IConfigurationSection configuration = microsoftFeatureDefinitionSections
177+
IConfigurationSection microsoftFeatureDefinitionConfiguration = microsoftFeatureDefinitionSections
175178
.LastOrDefault(section =>
176179
string.Equals(section[MicrosoftFeatureManagementFields.Id], featureName, StringComparison.OrdinalIgnoreCase));
177180

178-
if (configuration == null)
181+
if (microsoftFeatureDefinitionConfiguration == null)
179182
{
180183
return null;
181184
}
182185

183-
return ParseMicrosoftSchemaFeatureDefinition(configuration);
186+
return ParseMicrosoftSchemaFeatureDefinition(microsoftFeatureDefinitionConfiguration);
184187
}
185188

186189
private IEnumerable<IConfigurationSection> GetDotnetFeatureDefinitionSections()
@@ -518,7 +521,7 @@ private FeatureDefinition ParseMicrosoftSchemaFeatureDefinition(IConfigurationSe
518521
};
519522
}
520523

521-
private T ParseEnum<T>(string feature, string rawValue, string fieldKeyword)
524+
private static T ParseEnum<T>(string feature, string rawValue, string fieldKeyword)
522525
where T : struct, Enum
523526
{
524527
Debug.Assert(!string.IsNullOrEmpty(rawValue));
@@ -533,7 +536,7 @@ private T ParseEnum<T>(string feature, string rawValue, string fieldKeyword)
533536
return value;
534537
}
535538

536-
private double ParseDouble(string feature, string rawValue, string fieldKeyword)
539+
private static double ParseDouble(string feature, string rawValue, string fieldKeyword)
537540
{
538541
Debug.Assert(!string.IsNullOrEmpty(rawValue));
539542

@@ -547,7 +550,7 @@ private double ParseDouble(string feature, string rawValue, string fieldKeyword)
547550
return value;
548551
}
549552

550-
private bool ParseBool(string feature, string rawValue, string fieldKeyword)
553+
private static bool ParseBool(string feature, string rawValue, string fieldKeyword)
551554
{
552555
Debug.Assert(!string.IsNullOrEmpty(rawValue));
553556

0 commit comments

Comments
 (0)