Skip to content

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

  1. Call GetConfigSetting/GetConfigSettingAsync. If section is omitted or blank, the section actually used is TrackName (the application's resolved environment track), not an empty/root section (BR-EC-11).
  2. Build the dictionary key via BuildDictionaryKey(configKey, sectionToUse) — lowercases "{section}.{configKey}" (or just configKey when the section is blank) (BR-EC-12).
  3. Call LoadSection/LoadSectionAsync for sectionToUse.
  4. 3.1. This always runs the refresh check first: an empty global cache (or any call once the server's lastRefreshed timestamp has advanced, probed at most every 60 seconds) clears and reloads the whole cache — LastRefreshed is 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.
  5. 3.2. If the (lowercased) sectionToUse key is already recorded in the Sections cache, LoadSection/LoadSectionAsync returns immediately with no network call; otherwise it acquires SectionsSemaphore, re-checks Sections under 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 to Sections; a successful fetch with zero settings is removed from Sections if present and retried on the very next call rather than being stuck; a non-success HTTP result or a malformed response throws EnvironmentClientException out of this call. This loaded-state guard applies only to a nonempty sectionToUse; since here it is always TrackName or a caller-supplied section (never the literal-empty-string root), it is reached normally (BR-EC-13).
  6. Origin: Steps 3.1–3.2 are implemented in Load section and Refresh check and cache reload, and executed on behalf of this operation.

  7. Look up the dictionary key via GetSettingFromDictionary.
  8. 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 bare key when 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).
  9. Origin: Step 4.1 is implemented in Developer override lookup and executed on behalf of this operation.

  10. If no override matched, look up configKey in the global Settings dictionary using the composed key. If absent, GetSettingFromDictionary returns an empty ConfigSetting (both ConfigKey and ConfigValue are null) rather than throwing.
  11. For the string overloads (GetSettingValue/GetSettingValueAsync), the result is setting.ConfigValue?.Value ?? "" — a missing setting silently returns an empty string, never an exception (BR-EC-18).
  12. For the generic overloads (GetSettingValue<T>/GetSettingValueAsync<T>), ThrowExceptionIfNullOnGeneric is called first. If ConfigValue is null (setting missing), it throws EnvironmentClientException("Environment setting does not exist. It must exist when calling the generic implementation of {methodName}.") regardless of T — this guard is not itself generic, it only checks for a null ConfigValue (BR-EC-19). If present, ConfigSettingExtensions.GetValue<T>() converts the stored string via Convert.ChangeType(setting.ConfigValue.Value, typeof(T)); a conversion failure propagates Convert.ChangeType's exception (for example InvalidCastException or FormatException). There is no supplied/injectable TypeConverter parameter 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.cs
  • src/SAIF.NetCore.EnvironmentClient/SAIF.NetCore.EnvironmentClient/SAIF.NetCore.EnvironmentClient/Extensions/ConfigSettingExtensions.cs