@@ -27,9 +27,6 @@ public sealed class ConfigurationFeatureDefinitionProvider : IFeatureDefinitionP
27
27
private readonly ConcurrentDictionary < string , FeatureDefinition > _definitions ;
28
28
private IDisposable _changeSubscription ;
29
29
private int _stale = 0 ;
30
- private long _initialized = 0 ;
31
- private bool _microsoftFeatureManagementSchemaEnabled ;
32
- private readonly object _lock = new object ( ) ;
33
30
34
31
const string ParseValueErrorString = "Invalid setting '{0}' with value '{1}' for feature '{2}'." ;
35
32
@@ -84,18 +81,15 @@ public Task<FeatureDefinition> GetFeatureDefinitionAsync(string featureName)
84
81
throw new ArgumentException ( $ "The value '{ ConfigurationPath . KeyDelimiter } ' is not allowed in the feature name.", nameof ( featureName ) ) ;
85
82
}
86
83
87
- EnsureInit ( ) ;
88
-
89
84
if ( Interlocked . Exchange ( ref _stale , 0 ) != 0 )
90
85
{
91
86
_definitions . Clear ( ) ;
92
87
}
93
88
94
- //
95
- // Query by feature name
96
- FeatureDefinition definition = _definitions . GetOrAdd ( featureName , ( name ) => ReadFeatureDefinition ( name ) ) ;
97
-
98
- return Task . FromResult ( definition ) ;
89
+ return Task . FromResult (
90
+ _definitions . GetOrAdd (
91
+ featureName ,
92
+ ( _ ) => GetMicrosoftSchemaFeatureDefinition ( featureName ) ?? GetDotnetSchemaFeatureDefinition ( featureName ) ) ) ;
99
93
}
100
94
101
95
/// <summary>
@@ -109,18 +103,16 @@ public Task<FeatureDefinition> GetFeatureDefinitionAsync(string featureName)
109
103
public async IAsyncEnumerable < FeatureDefinition > GetAllFeatureDefinitionsAsync ( )
110
104
#pragma warning restore CS1998
111
105
{
112
- EnsureInit ( ) ;
113
-
114
106
if ( Interlocked . Exchange ( ref _stale , 0 ) != 0 )
115
107
{
116
108
_definitions . Clear ( ) ;
117
109
}
118
110
119
- //
120
- // Iterate over all features registered in the system at initial invocation time
121
- foreach ( IConfigurationSection featureSection in GetFeatureDefinitionSections ( ) )
111
+ IEnumerable < IConfigurationSection > microsoftFeatureDefinitionSections = GetMicrosoftFeatureDefinitionSections ( ) ;
112
+
113
+ foreach ( IConfigurationSection featureSection in microsoftFeatureDefinitionSections )
122
114
{
123
- string featureName = GetFeatureName ( featureSection ) ;
115
+ string featureName = featureSection [ MicrosoftFeatureManagementFields . Id ] ;
124
116
125
117
if ( string . IsNullOrEmpty ( featureName ) )
126
118
{
@@ -129,80 +121,99 @@ public async IAsyncEnumerable<FeatureDefinition> GetAllFeatureDefinitionsAsync()
129
121
130
122
//
131
123
// Underlying IConfigurationSection data is dynamic so latest feature definitions are returned
132
- FeatureDefinition definition = _definitions . GetOrAdd ( featureName , ( _ ) => ReadFeatureDefinition ( featureSection ) ) ;
124
+ FeatureDefinition definition = _definitions . GetOrAdd ( featureName , ( _ ) => ParseMicrosoftSchemaFeatureDefinition ( featureSection ) ) ;
133
125
134
- //
135
- // Null cache entry possible if someone accesses non-existent flag directly (IsEnabled)
136
126
if ( definition != null )
137
127
{
138
128
yield return definition ;
139
129
}
140
130
}
141
- }
142
131
143
- private void EnsureInit ( )
144
- {
145
- if ( _initialized == 0 )
146
- {
147
- IConfiguration MicrosoftFeatureManagementConfigurationSection = _configuration
148
- . GetChildren ( )
149
- . FirstOrDefault ( section =>
150
- string . Equals (
151
- section . Key ,
152
- MicrosoftFeatureManagementFields . FeatureManagementSectionName ,
153
- StringComparison . OrdinalIgnoreCase ) ) ;
132
+ IEnumerable < IConfigurationSection > dotnetFeatureDefinitionSections = GetDotnetFeatureDefinitionSections ( ) ;
154
133
155
- bool hasMicrosoftFeatureManagementSchema = MicrosoftFeatureManagementConfigurationSection != null ;
134
+ foreach ( IConfigurationSection featureSection in dotnetFeatureDefinitionSections )
135
+ {
136
+ string featureName = featureSection . Key ;
156
137
157
- if ( MicrosoftFeatureManagementConfigurationSection == null & RootConfigurationFallbackEnabled )
138
+ if ( string . IsNullOrEmpty ( featureName ) )
158
139
{
159
- IConfiguration featureFlagsSection = _configuration
160
- . GetChildren ( )
161
- . FirstOrDefault ( section =>
162
- string . Equals (
163
- section . Key ,
164
- MicrosoftFeatureManagementFields . FeatureFlagsSectionName ,
165
- StringComparison . OrdinalIgnoreCase ) ) ;
166
-
167
- hasMicrosoftFeatureManagementSchema = featureFlagsSection != null ;
140
+ continue ;
168
141
}
169
142
170
- lock ( _lock )
171
- {
172
- if ( Interlocked . Read ( ref _initialized ) == 0 )
173
- {
174
- _microsoftFeatureManagementSchemaEnabled = hasMicrosoftFeatureManagementSchema ;
143
+ //
144
+ // Underlying IConfigurationSection data is dynamic so latest feature definitions are returned
145
+ FeatureDefinition definition = _definitions . GetOrAdd ( featureName , ( _ ) => ParseDotnetSchemaFeatureDefinition ( featureSection ) ) ;
175
146
176
- Interlocked . Exchange ( ref _initialized , 1 ) ;
177
- }
147
+ if ( definition != null )
148
+ {
149
+ yield return definition ;
178
150
}
179
151
}
180
152
}
181
153
182
- private FeatureDefinition ReadFeatureDefinition ( string featureName )
154
+ private FeatureDefinition GetDotnetSchemaFeatureDefinition ( string featureName )
183
155
{
184
- IConfigurationSection configuration = GetFeatureDefinitionSections ( )
185
- . FirstOrDefault ( section => string . Equals ( GetFeatureName ( section ) , featureName , StringComparison . OrdinalIgnoreCase ) ) ;
156
+ IEnumerable < IConfigurationSection > dotnetFeatureDefinitionSections = GetDotnetFeatureDefinitionSections ( ) ;
157
+
158
+ IConfigurationSection configuration = dotnetFeatureDefinitionSections
159
+ . FirstOrDefault ( section =>
160
+ string . Equals ( section . Key , featureName , StringComparison . OrdinalIgnoreCase ) ) ;
186
161
187
162
if ( configuration == null )
188
163
{
189
164
return null ;
190
165
}
191
166
192
- return ReadFeatureDefinition ( configuration ) ;
167
+ return ParseDotnetSchemaFeatureDefinition ( configuration ) ;
193
168
}
194
169
195
- private FeatureDefinition ReadFeatureDefinition ( IConfigurationSection configurationSection )
170
+ private FeatureDefinition GetMicrosoftSchemaFeatureDefinition ( string featureName )
196
171
{
197
- if ( _microsoftFeatureManagementSchemaEnabled )
172
+ IEnumerable < IConfigurationSection > microsoftFeatureDefinitionSections = GetMicrosoftFeatureDefinitionSections ( ) ;
173
+
174
+ IConfigurationSection configuration = microsoftFeatureDefinitionSections
175
+ . FirstOrDefault ( section =>
176
+ string . Equals ( section [ MicrosoftFeatureManagementFields . Id ] , featureName , StringComparison . OrdinalIgnoreCase ) ) ;
177
+
178
+ if ( configuration == null )
179
+ {
180
+ return null ;
181
+ }
182
+
183
+ return ParseMicrosoftSchemaFeatureDefinition ( configuration ) ;
184
+ }
185
+
186
+ private IEnumerable < IConfigurationSection > GetDotnetFeatureDefinitionSections ( )
187
+ {
188
+ IConfigurationSection featureManagementConfigurationSection = _configuration . GetSection ( DotnetFeatureManagementFields . FeatureManagementSectionName ) ;
189
+
190
+ if ( featureManagementConfigurationSection . Exists ( ) )
191
+ {
192
+ return featureManagementConfigurationSection . GetChildren ( ) ;
193
+ }
194
+
195
+ //
196
+ // Root configuration fallback only applies to .NET schema.
197
+ // If Microsoft schema can be found, root configuration fallback will not be effective.
198
+ if ( RootConfigurationFallbackEnabled &&
199
+ ! _configuration . GetChildren ( )
200
+ . Any ( section =>
201
+ string . Equals ( section . Key , MicrosoftFeatureManagementFields . FeatureManagementSectionName , StringComparison . OrdinalIgnoreCase ) ) )
198
202
{
199
- return ParseMicrosoftFeatureDefinition ( configurationSection ) ;
203
+ return _configuration . GetChildren ( ) ;
200
204
}
201
205
202
- return ParseDotnetFeatureDefinition ( configurationSection ) ;
206
+ return Enumerable . Empty < IConfigurationSection > ( ) ;
203
207
}
204
208
205
- private FeatureDefinition ParseDotnetFeatureDefinition ( IConfigurationSection configurationSection )
209
+ private IEnumerable < IConfigurationSection > GetMicrosoftFeatureDefinitionSections ( )
210
+ {
211
+ return _configuration . GetSection ( MicrosoftFeatureManagementFields . FeatureManagementSectionName )
212
+ . GetSection ( MicrosoftFeatureManagementFields . FeatureFlagsSectionName )
213
+ . GetChildren ( ) ;
214
+ }
215
+
216
+ private FeatureDefinition ParseDotnetSchemaFeatureDefinition ( IConfigurationSection configurationSection )
206
217
{
207
218
/*
208
219
@@ -229,7 +240,7 @@ We support
229
240
230
241
*/
231
242
232
- string featureName = GetFeatureName ( configurationSection ) ;
243
+ string featureName = configurationSection . Key ;
233
244
234
245
var enabledFor = new List < FeatureFilterConfiguration > ( ) ;
235
246
@@ -293,7 +304,7 @@ We support
293
304
} ;
294
305
}
295
306
296
- private FeatureDefinition ParseMicrosoftFeatureDefinition ( IConfigurationSection configurationSection )
307
+ private FeatureDefinition ParseMicrosoftSchemaFeatureDefinition ( IConfigurationSection configurationSection )
297
308
{
298
309
/*
299
310
@@ -323,7 +334,7 @@ private FeatureDefinition ParseMicrosoftFeatureDefinition(IConfigurationSection
323
334
324
335
*/
325
336
326
- string featureName = GetFeatureName ( configurationSection ) ;
337
+ string featureName = configurationSection [ MicrosoftFeatureManagementFields . Id ] ;
327
338
328
339
var enabledFor = new List < FeatureFilterConfiguration > ( ) ;
329
340
@@ -354,13 +365,9 @@ private FeatureDefinition ParseMicrosoftFeatureDefinition(IConfigurationSection
354
365
{
355
366
string rawRequirementType = conditionsSection [ MicrosoftFeatureManagementFields . RequirementType ] ;
356
367
357
- //
358
- // If requirement type is specified, parse it and set the requirementType variable
359
- if ( ! string . IsNullOrEmpty ( rawRequirementType ) && ! Enum . TryParse ( rawRequirementType , ignoreCase : true , out requirementType ) )
368
+ if ( ! string . IsNullOrEmpty ( rawRequirementType ) )
360
369
{
361
- throw new FeatureManagementException (
362
- FeatureManagementError . InvalidConfigurationSetting ,
363
- $ "Invalid value '{ rawRequirementType } ' for field '{ MicrosoftFeatureManagementFields . RequirementType } ' of feature '{ featureName } '.") ;
370
+ requirementType = ParseEnum < RequirementType > ( featureName , rawRequirementType , MicrosoftFeatureManagementFields . RequirementType ) ;
364
371
}
365
372
366
373
featureStatus = FeatureStatus . Conditional ;
@@ -512,59 +519,6 @@ private FeatureDefinition ParseMicrosoftFeatureDefinition(IConfigurationSection
512
519
} ;
513
520
}
514
521
515
- private string GetFeatureName ( IConfigurationSection section )
516
- {
517
- if ( _microsoftFeatureManagementSchemaEnabled )
518
- {
519
- return section [ MicrosoftFeatureManagementFields . Id ] ;
520
- }
521
-
522
- return section . Key ;
523
- }
524
-
525
- private IEnumerable < IConfigurationSection > GetFeatureDefinitionSections ( )
526
- {
527
- if ( ! _configuration . GetChildren ( ) . Any ( ) )
528
- {
529
- Logger ? . LogDebug ( $ "Configuration is empty.") ;
530
-
531
- return Enumerable . Empty < IConfigurationSection > ( ) ;
532
- }
533
-
534
- IConfiguration featureManagementConfigurationSection = _configuration
535
- . GetChildren ( )
536
- . FirstOrDefault ( section =>
537
- string . Equals (
538
- section . Key ,
539
- _microsoftFeatureManagementSchemaEnabled ?
540
- MicrosoftFeatureManagementFields . FeatureManagementSectionName :
541
- DotnetFeatureManagementFields . FeatureManagementSectionName ,
542
- StringComparison . OrdinalIgnoreCase ) ) ;
543
-
544
- if ( featureManagementConfigurationSection == null )
545
- {
546
- if ( RootConfigurationFallbackEnabled )
547
- {
548
- featureManagementConfigurationSection = _configuration ;
549
- }
550
- else
551
- {
552
- Logger ? . LogDebug ( $ "No feature management configuration section was found.") ;
553
-
554
- return Enumerable . Empty < IConfigurationSection > ( ) ;
555
- }
556
- }
557
-
558
- if ( _microsoftFeatureManagementSchemaEnabled )
559
- {
560
- IConfigurationSection featureFlagsSection = featureManagementConfigurationSection . GetSection ( MicrosoftFeatureManagementFields . FeatureFlagsSectionName ) ;
561
-
562
- return featureFlagsSection . GetChildren ( ) ;
563
- }
564
-
565
- return featureManagementConfigurationSection . GetChildren ( ) ;
566
- }
567
-
568
522
private T ParseEnum < T > ( string feature , string rawValue , string fieldKeyword )
569
523
where T : struct , Enum
570
524
{
0 commit comments