Get setting value¶
| Field | Value |
|---|---|
| Owner | GetSettingValue/GetSettingValueAsync (string overloads) and GetSettingValue<T>/GetSettingValueAsync<T> (generic overloads), both with and without a section parameter |
| Trigger | Consumer call |
| Inputs | configKey, optional section |
| Outputs | The resolved string value ("" when missing), or the typed value T (exception when missing) |
Steps¶
- Call
GetConfigSetting/GetConfigSettingAsync. Ifsectionis omitted or blank, the section actually used isTrackName(the application's resolved environment track), not an empty/root section (BR-EC-11). - Build the dictionary key via
BuildDictionaryKey(configKey, sectionToUse)— lowercases"{section}.{configKey}"(or justconfigKeywhen the section is blank) (BR-EC-12). - Call
LoadSection/LoadSectionAsyncforsectionToUse. - 3.1. This always runs the refresh check first: an empty global cache (or any call once the server's
lastRefreshedtimestamp has advanced, probed at most every 60 seconds) clears and reloads the whole cache —LastRefreshedis set before that reload runs, not after it completes, so a failure partway through is not guaranteed to be retried on the very next call; see Load section step 1. - 3.2. If the (lowercased)
sectionToUsekey is already recorded in theSectionscache,LoadSection/LoadSectionAsyncreturns immediately with no network call; otherwise it acquiresSectionsSemaphore, re-checksSectionsunder the lock (so a concurrent caller that just finished loading the same section is not re-fetched), and only then calls the Environment API. A successful fetch with 1+ settings adds the section toSections; a successful fetch with zero settings is removed fromSectionsif present and retried on the very next call rather than being stuck; a non-success HTTP result or a malformed response throwsEnvironmentClientExceptionout of this call. This loaded-state guard applies only to a nonemptysectionToUse; since here it is alwaysTrackNameor a caller-supplied section (never the literal-empty-string root), it is reached normally (BR-EC-13). -
Origin: Steps 3.1–3.2 are implemented in Load section and Refresh check and cache reload, and executed on behalf of this operation.
- Look up the dictionary key via
GetSettingFromDictionary. - 4.1. A developer override file configured for this process is re-read and parsed on every call; if it contains an entry whose formatted key (
"{section}.{key}", or barekeywhen section is empty) matches (case-insensitive), that entry's value is returned immediately, bypassing the Environment API-backed dictionary entirely. Parse errors on the override file are silently swallowed. The override lookup runs after step 3 has already triggered the network-backed load — an override does not avoid the bootstrap/refresh network dependency, it only changes which value is returned once loading has happened (BR-EC-09). -
Origin: Step 4.1 is implemented in Developer override lookup and executed on behalf of this operation.
- If no override matched, look up
configKeyin the globalSettingsdictionary using the composed key. If absent,GetSettingFromDictionaryreturns an emptyConfigSetting(bothConfigKeyandConfigValuearenull) rather than throwing. - For the string overloads (
GetSettingValue/GetSettingValueAsync), the result issetting.ConfigValue?.Value ?? ""— a missing setting silently returns an empty string, never an exception (BR-EC-18). - For the generic overloads (
GetSettingValue<T>/GetSettingValueAsync<T>),ThrowExceptionIfNullOnGenericis called first. IfConfigValueisnull(setting missing), it throwsEnvironmentClientException("Environment setting does not exist. It must exist when calling the generic implementation of {methodName}.")regardless ofT— this guard is not itself generic, it only checks for anullConfigValue(BR-EC-19). If present,ConfigSettingExtensions.GetValue<T>()converts the stored string viaConvert.ChangeType(setting.ConfigValue.Value, typeof(T)); a conversion failure propagatesConvert.ChangeType's exception (for exampleInvalidCastExceptionorFormatException). There is no supplied/injectableTypeConverterparameter on any overload.
Process flow¶
The diagram below shows the developer-override, present-key, and absent-key paths converging on a single setting.ConfigValue == null guard before any string or generic handling runs — an override entry or a dictionary hit can itself carry a null ConfigValue, so key presence alone does not guarantee a usable value.
flowchart TD
A["GetSettingValue(configKey, section?)"] --> B["sectionToUse = section or TrackName"]
B --> C["BuildDictionaryKey:<br>lowercase section.configKey"]
C --> D["Load section(sectionToUse)"]
D --> E["Developer override lookup"]
E -->|override found| F["setting = override entry"]
E -->|no override| G{"Key in global<br>Settings dictionary?"}
G -- Yes --> H["setting = Settings[key]"]
G -- No --> I["setting = new ConfigSetting()<br>(ConfigValue is null)"]
F --> J{"setting.ConfigValue<br>== null?"}
H --> J
I --> J
J -- Yes --> K{"Generic overload?"}
K -- Yes --> L["EnvironmentClientException:<br>setting does not exist"]
K -- No --> M["Return setting.ConfigValue?.Value<br>?? #quot;#quot; (empty string)"]
J -- No --> N{"Generic overload?"}
N -- Yes --> O["Convert.ChangeType(ConfigValue.Value, T)"]
N -- No --> P["Return setting.ConfigValue.Value<br>?? #quot;#quot; (empty string)"]
Sources¶
src/SAIF.NetCore.EnvironmentClient/SAIF.NetCore.EnvironmentClient/SAIF.NetCore.EnvironmentClient/BaseEnvironmentClient.cssrc/SAIF.NetCore.EnvironmentClient/SAIF.NetCore.EnvironmentClient/SAIF.NetCore.EnvironmentClient/Extensions/ConfigSettingExtensions.cs