@@ -27,9 +27,6 @@ public sealed class ConfigurationFeatureDefinitionProvider : IFeatureDefinitionP
2727 private readonly ConcurrentDictionary < string , FeatureDefinition > _definitions ;
2828 private IDisposable _changeSubscription ;
2929 private int _stale = 0 ;
30- private long _initialized = 0 ;
31- private bool _microsoftFeatureManagementSchemaEnabled ;
32- private readonly object _lock = new object ( ) ;
3330
3431 const string ParseValueErrorString = "Invalid setting '{0}' with value '{1}' for feature '{2}'." ;
3532
@@ -84,18 +81,15 @@ public Task<FeatureDefinition> GetFeatureDefinitionAsync(string featureName)
8481 throw new ArgumentException ( $ "The value '{ ConfigurationPath . KeyDelimiter } ' is not allowed in the feature name.", nameof ( featureName ) ) ;
8582 }
8683
87- EnsureInit ( ) ;
88-
8984 if ( Interlocked . Exchange ( ref _stale , 0 ) != 0 )
9085 {
9186 _definitions . Clear ( ) ;
9287 }
9388
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 ) ) ) ;
9993 }
10094
10195 /// <summary>
@@ -109,18 +103,16 @@ public Task<FeatureDefinition> GetFeatureDefinitionAsync(string featureName)
109103 public async IAsyncEnumerable < FeatureDefinition > GetAllFeatureDefinitionsAsync ( )
110104#pragma warning restore CS1998
111105 {
112- EnsureInit ( ) ;
113-
114106 if ( Interlocked . Exchange ( ref _stale , 0 ) != 0 )
115107 {
116108 _definitions . Clear ( ) ;
117109 }
118110
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 )
122114 {
123- string featureName = GetFeatureName ( featureSection ) ;
115+ string featureName = featureSection [ MicrosoftFeatureManagementFields . Id ] ;
124116
125117 if ( string . IsNullOrEmpty ( featureName ) )
126118 {
@@ -129,80 +121,99 @@ public async IAsyncEnumerable<FeatureDefinition> GetAllFeatureDefinitionsAsync()
129121
130122 //
131123 // 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 ) ) ;
133125
134- //
135- // Null cache entry possible if someone accesses non-existent flag directly (IsEnabled)
136126 if ( definition != null )
137127 {
138128 yield return definition ;
139129 }
140130 }
141- }
142131
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 ( ) ;
154133
155- bool hasMicrosoftFeatureManagementSchema = MicrosoftFeatureManagementConfigurationSection != null ;
134+ foreach ( IConfigurationSection featureSection in dotnetFeatureDefinitionSections )
135+ {
136+ string featureName = featureSection . Key ;
156137
157- if ( MicrosoftFeatureManagementConfigurationSection == null & RootConfigurationFallbackEnabled )
138+ if ( string . IsNullOrEmpty ( featureName ) )
158139 {
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 ;
168141 }
169142
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 ) ) ;
175146
176- Interlocked . Exchange ( ref _initialized , 1 ) ;
177- }
147+ if ( definition != null )
148+ {
149+ yield return definition ;
178150 }
179151 }
180152 }
181153
182- private FeatureDefinition ReadFeatureDefinition ( string featureName )
154+ private FeatureDefinition GetDotnetSchemaFeatureDefinition ( string featureName )
183155 {
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 ) ) ;
186161
187162 if ( configuration == null )
188163 {
189164 return null ;
190165 }
191166
192- return ReadFeatureDefinition ( configuration ) ;
167+ return ParseDotnetSchemaFeatureDefinition ( configuration ) ;
193168 }
194169
195- private FeatureDefinition ReadFeatureDefinition ( IConfigurationSection configurationSection )
170+ private FeatureDefinition GetMicrosoftSchemaFeatureDefinition ( string featureName )
196171 {
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 ) ) )
198202 {
199- return ParseMicrosoftFeatureDefinition ( configurationSection ) ;
203+ return _configuration . GetChildren ( ) ;
200204 }
201205
202- return ParseDotnetFeatureDefinition ( configurationSection ) ;
206+ return Enumerable . Empty < IConfigurationSection > ( ) ;
203207 }
204208
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 )
206217 {
207218 /*
208219
@@ -229,7 +240,7 @@ We support
229240
230241 */
231242
232- string featureName = GetFeatureName ( configurationSection ) ;
243+ string featureName = configurationSection . Key ;
233244
234245 var enabledFor = new List < FeatureFilterConfiguration > ( ) ;
235246
@@ -293,7 +304,7 @@ We support
293304 } ;
294305 }
295306
296- private FeatureDefinition ParseMicrosoftFeatureDefinition ( IConfigurationSection configurationSection )
307+ private FeatureDefinition ParseMicrosoftSchemaFeatureDefinition ( IConfigurationSection configurationSection )
297308 {
298309 /*
299310
@@ -323,7 +334,7 @@ private FeatureDefinition ParseMicrosoftFeatureDefinition(IConfigurationSection
323334
324335 */
325336
326- string featureName = GetFeatureName ( configurationSection ) ;
337+ string featureName = configurationSection [ MicrosoftFeatureManagementFields . Id ] ;
327338
328339 var enabledFor = new List < FeatureFilterConfiguration > ( ) ;
329340
@@ -354,13 +365,9 @@ private FeatureDefinition ParseMicrosoftFeatureDefinition(IConfigurationSection
354365 {
355366 string rawRequirementType = conditionsSection [ MicrosoftFeatureManagementFields . RequirementType ] ;
356367
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 ) )
360369 {
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 ) ;
364371 }
365372
366373 featureStatus = FeatureStatus . Conditional ;
@@ -512,59 +519,6 @@ private FeatureDefinition ParseMicrosoftFeatureDefinition(IConfigurationSection
512519 } ;
513520 }
514521
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-
568522 private T ParseEnum < T > ( string feature , string rawValue , string fieldKeyword )
569523 where T : struct , Enum
570524 {
0 commit comments