--- source_files: - Common/DTS.Common.SettingsDB/UserSetting.cs - Common/DTS.Common.SettingsDB/Setting.cs - Common/DTS.Common.SettingsDB/GlobalSetting.cs - Common/DTS.Common.SettingsDB/SettingsDB.cs generated_at: "2026-04-16T11:31:14.800080+00:00" model: "zai-org/GLM-5-FP8" schema_version: 1 sha256: "0690ee9f2887691d" --- # Documentation: DTS.Common.Settings ## 1. Purpose This module provides a persistent settings management system that stores and retrieves application configuration values in a SQL database. It supports two categories of settings: **global settings** (application-wide, identified by the `"SYSTEM"` user) and **user-specific settings** (scoped to individual users). The `SettingsDB` class serves as the primary facade, implementing a thread-safe singleton pattern with in-memory caching to minimize database round-trips. Settings are serialized as strings in the database, with helper methods provided for type conversion to/from `int`, `double`, `bool`, and arrays. --- ## 2. Public Interface ### `Setting` (Abstract Base Class) The abstract base class for all setting types. **Properties:** - `string PropertyId` — The string identifier for the setting. - `PropertyTypes PropertyType` — Either `PropertyTypes.User` (1) or `PropertyTypes.Global` (2). - `string PropertyValue` — The current value of the setting. - `string UserId` — The user identifier; `"SYSTEM"` for global settings. **Methods:** - `void SetValue(string value)` — Updates the in-memory value and persists it to the database via `StoreInDB()`. **Nested Types:** - `enum PropertyTypes` — Values: `User = 1`, `Global = 2`. --- ### `GlobalSetting` (Class, inherits `Setting`) Represents an application-wide setting. **Constructors:** - `GlobalSetting(string id, string defaultPropertyValue)` — Creates or retrieves a global setting with the given ID and default value. **Static Methods:** - `GlobalSetting ReadXML(System.Xml.XmlElement root)` — Parses an XML element to construct a `GlobalSetting`. Expects child elements matching `XMLFields.NAME` and `XMLFields.VALUE`. - `GlobalSetting[] GetAllGlobalSettings()` — Retrieves all global settings from the database. Returns an empty array on failure. **Nested Types:** - `enum XMLFields` — Values: `NAME`, `VALUE`. Used for XML parsing. - `class NullDefaultValueException : NullReferenceException` — Thrown when a setting doesn't exist and no default value is provided. --- ### `UserSetting` (Class, inherits `Setting`) Represents a user-specific setting. **Constructors:** - `UserSetting(string id, string defaultValue, string user)` — Creates or retrieves a user-specific setting for the given user. --- ### `SettingsDB` (Static Facade Class) The primary interface for interacting with settings. Implements a thread-safe singleton with caching. **Static Methods:** | Method | Description | |--------|-------------| | `void RefreshSettings()` | Clears the in-memory settings cache. | | `string GetUserValue(string id, string defaultValue, string user)` | Retrieves a user-specific value; creates with default if not exists. | | `string GetGlobalValue(string id, string defaultValue, bool useCache = true)` | Retrieves a global value; creates with default if not exists. Optionally bypasses cache. | | `void GetAllGlobalValues()` | Loads all global settings into the cache. | | `double GetGlobalValueDouble(string id, double defaultValue)` | Retrieves a global value parsed as `double`. | | `int GetGlobalValueInt(string id, int defaultValue)` | Retrieves a global value parsed as `int`. | | `bool GetGlobalValueBool(string id, bool defaultValue, bool useCache = true)` | Retrieves a global value parsed as `bool`. | | `double[] GetGlobalValueDoubleArray(string id, double[] defaultValues)` | Retrieves an array of doubles stored as separate keys (`{id}_x_Count`, `{id}_x_0`, etc.). | | `int[] GetGlobalValueIntArray(string id, int[] defaultValues)` | Retrieves an array of ints stored as separate keys. | | `void SetUserValue(string id, string value, string user)` | Sets a user-specific value in the database. | | `void SetGlobalValue(string id, string value)` | Sets a global string value. | | `void SetGlobalValueDouble(string id, double value)` | Sets a global double value. | | `void SetGlobalValueInt(string id, int value)` | Sets a global int value. | | `void SetGlobalValueBoolean(string id, bool value)` | Sets a global bool value. | | `void SetGlobalValueDoubleArray(string id, double[] values)` | Stores a double array as multiple keys. | | `void SetGlobalValueIntArray(string id, int[] values)` | Stores an int array as multiple keys. | --- ## 3. Invariants 1. **Thread Safety**: All public methods in `SettingsDB` acquire a lock on `LOCK_OBJECT` before accessing `_settingsLookup`. The singleton instance is lazily initialized under lock. 2. **Key Uniqueness**: User-specific settings use a composite key format `{id}_{user}` in the cache; global settings use `{id}` directly. 3. **Global Settings Identity**: All `GlobalSetting` instances use `UserId = "SYSTEM"` (defined as a private constant). 4. **Database Synchronization**: Calling `SetValue()` on any `Setting` instance immediately persists the value to the database via `StoreInDB()`. 5. **Auto-Creation on Missing**: When a setting is retrieved but doesn't exist in the database, it is automatically created with the provided default value and persisted. 6. **Array Storage Convention**: Arrays are stored as multiple database records: - `{id}_x_Count` — The number of elements. - `{id}_x_{i}` — The value at index `i`. 7. **Culture Invariance**: All numeric-to-string conversions use `CultureInfo.InvariantCulture`. --- ## 4. Dependencies ### This Module Depends On: - `System.Data` — For `CommandType`, `SqlParameter`, `SqlDbType`, `ParameterDirection`. - `System.Data.SqlClient` — For `SqlConnection`/`SqlCommand` operations (implied via `SqlParameter`). - `DTS.Common.Storage` — Provides: - `DbOperations.GetSQLCommand(bool)` - `DbOperations.Connection.QueryDataSet(SqlCommand)` - `DbOperationsEnum.StoredProcedure` (enum with `sp_SettingsGet`, `sp_SettingsUpdateInsert`) - `DbOperations.Settings.UserFields.PropertyValue` (enum) - `DTS.Common.Utilities.Logging` — Provides `APILogger.LogException(Exception)` and `APILogger.Log(string, Exception)`. ### Database Dependencies: - Stored Procedure: `sp_SettingsGet` — Retrieves settings by `@UserId` and `@PropertyId`. - Stored Procedure: `sp_SettingsUpdateInsert` — Inserts or updates a setting. Parameters: `@PropertyId`, `@PropertyType`, `@PropertyValue`, `@UserId`, `@new_id` (output), `@errorNumber` (output), `@errorMessage` (output). --- ## 5. Gotchas 1. **Silent Exception Swallowing in `UserSetting.GetPropertyValue`**: All exceptions are caught and silently ignored; the method falls back to the default value without logging. This can mask database connectivity issues or data corruption. 2. **Unused Error Output Parameters**: In `Setting.StoreInDB()`, the stored procedure returns `@errorNumber` and `@errorMessage`, but the code checks if `errorNumber != 0` and then does nothing (empty block). Errors from the database are silently ignored. 3. **Max Length Constraint Not Enforced in Code**: The `PropertyValue` property documentation states "there is a max length" but no validation occurs before database insertion. The stored procedure parameters specify length 255 for string values. 4. **Redundant `cmd.Connection.Dispose()`**: All database methods call `cmd.Connection.Dispose()` inside a `finally` block, but the `cmd` is already inside a `using` statement. This is redundant and potentially confusing. 5. **Backward Compatibility Hack for `ImportCreateDynamicGroups`**: `SettingsDB` contains special-case logic for migrating from `CSV_IMPORT_CREATE_DYNAMIC_GROUPS` to `IMPORT_CREATE_DYNAMIC_GROUPS`. When setting the new key, it also sets the old key for pre-Version 93 client compatibility. This is documented in comments referencing issue 36831. 6. **`GetGlobalValueBool` Parsing Logic Error**: The method contains `return !bool.TryParse(sValue, out b) ? b : b;` — both branches return `b`, making the conditional meaningless. It should likely return `defaultValue` on parse failure. 7. **`NullDefaultValueException` Only Thrown by `GlobalSetting`**: `UserSetting.GetPropertyValue` does not throw this exception; it silently falls back to the default. Only `GlobalSetting.GetPropertyValue` throws `NullDefaultValueException` when the setting doesn't exist and `defaultValue` is null. 8. **TODO Comments Indicate Incomplete Work**: - `UserSetting.GetPropertyValue` line 42: `//TODO: handle exception properly` - `GlobalSetting.ProcessXMLElement` line 68: `// TODO: handle exception properly` 9. **Cache Bypass Only Affects Retrieval**: `GetGlobalValue(id, defaultValue, useCache: false)` bypasses the cache when reading, but `SetGlobalValue` always updates the cache. This can lead to stale cache entries if another process modifies the database directly.