Version: 1.12.0
Date: 2025-01-14
Status: ? In Development
Migrated application configuration from static appsettings.json files to a database-driven system, enabling:
- ? Dynamic Configuration: Update settings without restarting the application
- ?? Centralized Management: Single source of truth for all environments
- ? Category Organization: Logical grouping of related settings
- ? Type Safety: Strongly-typed value serialization/deserialization
- ? Audit Trail: Track who changed what and when
- ? Security: Mark sensitive settings for special handling
- ? Performance: In-memory caching with auto-refresh
CREATE TABLE ApplicationSettings (
Id INT PRIMARY KEY IDENTITY,
[Key] NVARCHAR(200) NOT NULL UNIQUE,
Value NVARCHAR(MAX) NOT NULL,
Category NVARCHAR(100) NOT NULL,
ValueType NVARCHAR(50) NOT NULL,
Description NVARCHAR(500) NULL,
IsSensitive BIT NOT NULL DEFAULT 0,
RequiresRestart BIT NOT NULL DEFAULT 0,
CreatedAtUtc DATETIMEOFFSET NOT NULL,
UpdatedAtUtc DATETIMEOFFSET NOT NULL,
UpdatedBy NVARCHAR(256) NULL
);
CREATE INDEX IX_ApplicationSettings_Key ON ApplicationSettings([Key]);
CREATE INDEX IX_ApplicationSettings_Category ON ApplicationSettings(Category);? ApplicationSettingsService (IApplicationSettingsService)
?
??? IMemoryCache (5-minute expiration)
?
??? SecureBootDbContext
?
??? SQL Server (ApplicationSettings table)
| Category | Setting Key | Type | Restart Required | Sensitive |
|---|---|---|---|---|
| QueueProcessor | QueueProcessor:Enabled |
bool | Yes | No |
| QueueProcessor | QueueProcessor:MaxMessages |
int | No | No |
| QueueProcessor | QueueProcessor:ProcessingInterval |
timespan | No | No |
| QueueProcessor | QueueProcessor:EmptyQueuePollInterval |
timespan | No | No |
| ClientUpdate | ClientUpdate:LatestVersion |
string | No | No |
| ClientUpdate | ClientUpdate:MinimumVersion |
string | No | No |
| ClientUpdate | ClientUpdate:IsUpdateRequired |
bool | No | No |
| ClientUpdate | ClientUpdate:DownloadUrl |
string | No | No |
| SecureBootReadiness | SecureBootReadiness:CertificateExpirationWarningDays |
int | No | No |
| SecureBootReadiness | SecureBootReadiness:CertificateExpirationCriticalDays |
int | No | No |
| SecureBootReadiness | SecureBootReadiness:RequireWindowsUEFICA2023 |
bool | No | No |
| SecureBootReadiness | SecureBootReadiness:RequireOemCertificatesValid |
bool | No | No |
Total Migrated: 12 settings across 3 categories
GET /api/SettingsResponse:
[
{
"id": 1,
"key": "QueueProcessor:Enabled",
"value": "true",
"category": "QueueProcessor",
"valueType": "bool",
"description": "Enable or disable the queue processor background service",
"isSensitive": false,
"requiresRestart": true,
"createdAtUtc": "2025-01-14T12:00:00Z",
"updatedAtUtc": "2025-01-14T12:00:00Z",
"updatedBy": null
}
]GET /api/Settings/category/{category}Example:
GET /api/Settings/category/QueueProcessorGET /api/Settings/key/{key}Example:
GET /api/Settings/key/ClientUpdate:LatestVersionPUT /api/Settings/key/{key}
Content-Type: application/json
{
"value": "1.6.0.0",
"updatedBy": "admin@contoso.com"
}Response:
{
"id": 5,
"key": "ClientUpdate:LatestVersion",
"value": "\"1.6.0.0\"",
"category": "ClientUpdate",
"valueType": "string",
"updatedAtUtc": "2025-01-14T14:30:00Z",
"updatedBy": "admin@contoso.com"
}POST /api/Settings/cache/refreshGET /api/Settings/restart-requiredInjecting the Service:
public class MyService
{
private readonly IApplicationSettingsService _settings;
public MyService(IApplicationSettingsService settings)
{
_settings = settings;
}
public async Task DoWorkAsync()
{
// Get setting with default
var maxMessages = await _settings.GetSettingAsync(
"QueueProcessor:MaxMessages",
defaultValue: 10);
// Get setting (nullable)
var latestVersion = await _settings.GetSettingAsync<string>(
"ClientUpdate:LatestVersion");
// Get all settings in category
var queueSettings = await _settings.GetCategorySettingsAsync(
"QueueProcessor");
}
}Updating Settings:
await _settings.SetSettingAsync(
"ClientUpdate:LatestVersion",
"1.6.0.0",
updatedBy: "admin@contoso.com");Get All Settings:
$settings = Invoke-RestMethod -Uri "https://api.local/api/Settings"
$settings | Format-Table Key, Value, CategoryUpdate Client Version:
$body = @{
value = "1.6.0.0"
updatedBy = "admin@contoso.com"
} | ConvertTo-Json
Invoke-RestMethod `
-Uri "https://api.local/api/Settings/key/ClientUpdate:LatestVersion" `
-Method Put `
-Body $body `
-ContentType "application/json"Get Queue Processor Settings:
$queueSettings = Invoke-RestMethod `
-Uri "https://api.local/api/Settings/category/QueueProcessor"
$queueSettings | Format-List- Cache Key Format:
AppSetting:{key} - Expiration: 5 minutes (sliding)
- Invalidation: On update (immediate)
1. GetSettingAsync()
?
2. Check IMemoryCache
?
3. ? FOUND ? Return cached value
?
4. ? NOT FOUND
?
5. Query Database
?
6. Cache result (5 min)
?
7. Return value
Update Flow:
1. SetSettingAsync()
?
2. Update Database
?
3. Remove from Cache
?
4. Next GetSettingAsync() will re-cache
cd SecureBootDashboard.Api
dotnet ef database updateVerification:
SELECT COUNT(*) FROM ApplicationSettings;
-- Expected: 12 rowsSELECT Category, COUNT(*) AS SettingCount
FROM ApplicationSettings
GROUP BY Category
ORDER BY Category;Expected Output:
Category SettingCount
------------------------ ------------
ClientUpdate 4
QueueProcessor 4
SecureBootReadiness 4
# Test GET all settings
Invoke-RestMethod -Uri "https://localhost:5001/api/Settings"
# Test GET by category
Invoke-RestMethod -Uri "https://localhost:5001/api/Settings/category/ClientUpdate"
# Test UPDATE
$body = @{ value = "11"; updatedBy = "test" } | ConvertTo-Json
Invoke-RestMethod `
-Uri "https://localhost:5001/api/Settings/key/QueueProcessor:MaxMessages" `
-Method Put `
-Body $body `
-ContentType "application/json"After migration is stable, you can optionally remove the migrated settings from appsettings.json to avoid confusion. However, keep them as fallback during transition.
Limitations:
- ? Requires application restart for changes
- ? Different configs per environment (dev, staging, prod)
- ? No audit trail
- ? Manual config file edits
- ? Deployment required to update
Advantages:
- ? Dynamic Updates: Most settings apply immediately (no restart)
- ? Single Source: One database table for all environments
- ? Audit Trail:
UpdatedByandUpdatedAtUtctracking - ? UI Management: Can build admin UI for configuration
- ? API Access: RESTful API for automation
- ? Versioning: Can track setting history (future enhancement)
- ? Security: Mark sensitive settings
- ? Performance: 5-minute cache reduces DB queries
Mark as Sensitive:
IsSensitive = trueBehavior:
- ? Masked in logs:
Setting updated to '***' - ? API returns masked values (future enhancement)
- ? Not cached (future enhancement)
Example:
UPDATE ApplicationSettings
SET IsSensitive = 1
WHERE [Key] LIKE '%Password%'
OR [Key] LIKE '%Secret%'
OR [Key] LIKE '%ApiKey%';Recommended:
- Add
[Authorize]attribute to SettingsController - Implement role-based access (Admin only)
- Audit all setting changes
- Encrypt sensitive values in database
Expected Performance:
- First Request: ~5ms (database query)
- Cached Requests: <1ms (memory cache)
- Cache Miss: ~5ms (re-query database)
Queries Per Hour (5-minute cache):
- 12 settings × (60 min / 5 min cache) = 144 queries/hour
- With cache: ~99% reduction in DB queries
-
Setting History
- Track all changes to settings
- Rollback capability
- Diff viewer
-
Admin UI
- Web-based settings editor
- Validation rules
- Bulk import/export
-
Environment-Specific Overrides
- Per-environment values
- Inheritance chain
-
Setting Groups
- Hierarchical organization
- Nested categories
-
Notifications
- Webhook on setting change
- Email alerts for critical settings
-
Encryption
- Transparent encryption for sensitive values
- Azure Key Vault integration
Symptoms: Update API succeeds but old value still used
Solutions:
- Check if
RequiresRestart = true - Manually refresh cache:
POST /api/Settings/cache/refresh - Restart application if required
Symptoms: ef database update fails
Solutions:
- Check database connectivity
- Verify SQL Server permissions
- Check for existing
ApplicationSettingstable
Symptoms: GetSettingAsync() returns null
Solutions:
- Verify setting exists in database:
SELECT * FROM ApplicationSettings WHERE [Key] = 'YourKey';
- Check cache: Clear and retry
- Verify ValueType is correct
- Apply database migration
- Verify seed data (12 rows)
- Test API endpoints
- Update QueueProcessorService to use settings service
- Update ClientUpdateController to use settings service
- Update SecureBootReadinessService to use settings service
- Add UI for settings management (optional)
- Add authorization to SettingsController
- Document for operations team
- Remove settings from appsettings.json (optional)
Backend:
Data/ApplicationSettingEntity.cs- Entity modelData/SecureBootDbContext.cs- DbContext configurationServices/IApplicationSettingsService.cs- Service interfaceServices/ApplicationSettingsService.cs- Service implementationControllers/SettingsController.cs- REST API
Migration:
Data/Migrations/*_AddApplicationSettingsTable.cs
Configuration:
Program.cs- Service registration
Last Updated: 2025-01-14
Author: Development Team
Status: ? Ready for Review