🔷 Configuration Management in C# with TuskLang
Configuration Management in C# with TuskLang
Overview
Effective configuration management is essential for modern applications. This guide covers comprehensive configuration patterns for C# applications using TuskLang, including hierarchical configurations, environment-specific settings, validation, and dynamic updates.
Table of Contents
1. Basic Configuration 2. Hierarchical Configuration 3. Environment-Specific Configuration 4. Configuration Validation 5. Dynamic Configuration 6. Secret Management 7. Configuration Providers 8. TuskLang Integration 9. Best Practices
Basic Configuration
Simple Configuration Setup
public class AppConfiguration
{
public string AppName { get; set; } = "MyApp";
public string Environment { get; set; } = "Development";
public int Port { get; set; } = 5000;
public bool EnableLogging { get; set; } = true;
public DatabaseConfig Database { get; set; } = new();
public ApiConfig Api { get; set; } = new();
}public class DatabaseConfig
{
public string ConnectionString { get; set; } = "";
public int MaxConnections { get; set; } = 100;
public TimeSpan CommandTimeout { get; set; } = TimeSpan.FromSeconds(30);
}
public class ApiConfig
{
public string BaseUrl { get; set; } = "https://api.example.com";
public string ApiKey { get; set; } = "";
public int RetryCount { get; set; } = 3;
public TimeSpan Timeout { get; set; } = TimeSpan.FromSeconds(30);
}
Configuration Loading
public class ConfigurationLoader
{
private readonly TuskLang _tuskLang; public ConfigurationLoader(TuskLang tuskLang)
{
_tuskLang = tuskLang;
}
public AppConfiguration LoadConfiguration()
{
var config = new AppConfiguration();
// Load from TuskLang configuration
config.AppName = _tuskLang.GetValue<string>("app.name", "MyApp");
config.Environment = _tuskLang.GetValue<string>("app.environment", "Development");
config.Port = _tuskLang.GetValue<int>("app.port", 5000);
config.EnableLogging = _tuskLang.GetValue<bool>("app.enableLogging", true);
// Load nested configurations
config.Database.ConnectionString = _tuskLang.GetValue<string>("database.connectionString", "");
config.Database.MaxConnections = _tuskLang.GetValue<int>("database.maxConnections", 100);
config.Database.CommandTimeout = TimeSpan.FromSeconds(
_tuskLang.GetValue<int>("database.commandTimeoutSeconds", 30));
config.Api.BaseUrl = _tuskLang.GetValue<string>("api.baseUrl", "https://api.example.com");
config.Api.ApiKey = _tuskLang.GetValue<string>("api.apiKey", "");
config.Api.RetryCount = _tuskLang.GetValue<int>("api.retryCount", 3);
config.Api.Timeout = TimeSpan.FromSeconds(
_tuskLang.GetValue<int>("api.timeoutSeconds", 30));
return config;
}
}
Configuration Registration
public static class ConfigurationExtensions
{
public static IServiceCollection AddAppConfiguration(
this IServiceCollection services, IConfiguration configuration)
{
// Bind configuration to strongly-typed objects
services.Configure<AppConfiguration>(configuration.GetSection("App"));
services.Configure<DatabaseConfig>(configuration.GetSection("Database"));
services.Configure<ApiConfig>(configuration.GetSection("Api")); // Register configuration as singleton
services.AddSingleton<AppConfiguration>(provider =>
{
var config = new AppConfiguration();
configuration.GetSection("App").Bind(config);
configuration.GetSection("Database").Bind(config.Database);
configuration.GetSection("Api").Bind(config.Api);
return config;
});
return services;
}
}
Hierarchical Configuration
Configuration Hierarchy
public class HierarchicalConfiguration
{
private readonly Dictionary<string, object> _config;
private readonly List<string> _configFiles; public HierarchicalConfiguration()
{
_config = new Dictionary<string, object>();
_configFiles = new List<string>();
}
public void LoadConfigurationHierarchy()
{
// Load configuration files in order of precedence
var configFiles = new[]
{
"config/default.tsk", // Base configuration
"config/environment.tsk", // Environment-specific
"config/local.tsk", // Local overrides
"config/secrets.tsk" // Secrets (if exists)
};
foreach (var file in configFiles)
{
if (File.Exists(file))
{
LoadConfigurationFile(file);
_configFiles.Add(file);
}
}
}
private void LoadConfigurationFile(string filePath)
{
var tuskLang = new TuskLang(filePath);
var config = tuskLang.GetAllValues();
foreach (var (key, value) in config)
{
_config[key] = value;
}
}
public T GetValue<T>(string key, T defaultValue = default)
{
if (_config.TryGetValue(key, out var value))
{
if (value is T typedValue)
return typedValue;
// Try to convert the value
try
{
return (T)Convert.ChangeType(value, typeof(T));
}
catch
{
return defaultValue;
}
}
return defaultValue;
}
public void SetValue<T>(string key, T value)
{
_config[key] = value;
}
public IEnumerable<string> GetLoadedFiles()
{
return _configFiles.AsReadOnly();
}
}
Configuration Override System
public class ConfigurationOverride
{
private readonly Dictionary<string, object> _overrides;
private readonly HierarchicalConfiguration _baseConfig; public ConfigurationOverride(HierarchicalConfiguration baseConfig)
{
_baseConfig = baseConfig;
_overrides = new Dictionary<string, object>();
}
public void OverrideValue<T>(string key, T value)
{
_overrides[key] = value;
}
public T GetValue<T>(string key, T defaultValue = default)
{
// Check overrides first
if (_overrides.TryGetValue(key, out var overrideValue))
{
if (overrideValue is T typedValue)
return typedValue;
try
{
return (T)Convert.ChangeType(overrideValue, typeof(T));
}
catch
{
return defaultValue;
}
}
// Fall back to base configuration
return _baseConfig.GetValue(key, defaultValue);
}
public void ClearOverrides()
{
_overrides.Clear();
}
public Dictionary<string, object> GetOverrides()
{
return new Dictionary<string, object>(_overrides);
}
}
Environment-Specific Configuration
Environment Configuration
public class EnvironmentConfiguration
{
private readonly string _environment;
private readonly TuskLang _tuskLang; public EnvironmentConfiguration(string environment)
{
_environment = environment;
_tuskLang = new TuskLang($"config/{environment}.tsk");
}
public AppConfiguration LoadEnvironmentConfig()
{
var config = new AppConfiguration
{
Environment = _environment
};
// Load environment-specific settings
config.AppName = _tuskLang.GetValue<string>("app.name", "MyApp");
config.Port = _tuskLang.GetValue<int>("app.port", 5000);
// Database configuration
config.Database.ConnectionString = _tuskLang.GetValue<string>("database.connectionString", "");
config.Database.MaxConnections = _tuskLang.GetValue<int>("database.maxConnections", 100);
// API configuration
config.Api.BaseUrl = _tuskLang.GetValue<string>("api.baseUrl", "");
config.Api.ApiKey = _tuskLang.GetValue<string>("api.apiKey", "");
return config;
}
public bool IsDevelopment => _environment.Equals("Development", StringComparison.OrdinalIgnoreCase);
public bool IsStaging => _environment.Equals("Staging", StringComparison.OrdinalIgnoreCase);
public bool IsProduction => _environment.Equals("Production", StringComparison.OrdinalIgnoreCase);
}
Environment-Specific Settings
public class EnvironmentSettings
{
public static AppConfiguration GetConfiguration(string environment)
{
var baseConfig = LoadBaseConfiguration();
var envConfig = LoadEnvironmentConfiguration(environment);
return MergeConfigurations(baseConfig, envConfig);
} private static AppConfiguration LoadBaseConfiguration()
{
var tuskLang = new TuskLang("config/base.tsk");
return new AppConfiguration
{
AppName = tuskLang.GetValue<string>("app.name", "MyApp"),
EnableLogging = tuskLang.GetValue<bool>("app.enableLogging", true),
Database = new DatabaseConfig
{
CommandTimeout = TimeSpan.FromSeconds(
tuskLang.GetValue<int>("database.commandTimeoutSeconds", 30))
},
Api = new ApiConfig
{
RetryCount = tuskLang.GetValue<int>("api.retryCount", 3),
Timeout = TimeSpan.FromSeconds(
tuskLang.GetValue<int>("api.timeoutSeconds", 30))
}
};
}
private static AppConfiguration LoadEnvironmentConfiguration(string environment)
{
var tuskLang = new TuskLang($"config/{environment}.tsk");
return new AppConfiguration
{
Environment = environment,
Port = tuskLang.GetValue<int>("app.port", 5000),
Database = new DatabaseConfig
{
ConnectionString = tuskLang.GetValue<string>("database.connectionString", ""),
MaxConnections = tuskLang.GetValue<int>("database.maxConnections", 100)
},
Api = new ApiConfig
{
BaseUrl = tuskLang.GetValue<string>("api.baseUrl", ""),
ApiKey = tuskLang.GetValue<string>("api.apiKey", "")
}
};
}
private static AppConfiguration MergeConfigurations(
AppConfiguration baseConfig, AppConfiguration envConfig)
{
return new AppConfiguration
{
AppName = envConfig.AppName ?? baseConfig.AppName,
Environment = envConfig.Environment,
Port = envConfig.Port,
EnableLogging = baseConfig.EnableLogging,
Database = new DatabaseConfig
{
ConnectionString = envConfig.Database.ConnectionString,
MaxConnections = envConfig.Database.MaxConnections,
CommandTimeout = baseConfig.Database.CommandTimeout
},
Api = new ApiConfig
{
BaseUrl = envConfig.Api.BaseUrl,
ApiKey = envConfig.Api.ApiKey,
RetryCount = baseConfig.Api.RetryCount,
Timeout = baseConfig.Api.Timeout
}
};
}
}
Configuration Validation
Configuration Validator
public class ConfigurationValidator
{
private readonly List<ValidationError> _errors; public ConfigurationValidator()
{
_errors = new List<ValidationError>();
}
public bool ValidateConfiguration(AppConfiguration config)
{
_errors.Clear();
// Validate required fields
ValidateRequired(config.AppName, "AppName");
ValidateRequired(config.Database.ConnectionString, "Database.ConnectionString");
ValidateRequired(config.Api.BaseUrl, "Api.BaseUrl");
ValidateRequired(config.Api.ApiKey, "Api.ApiKey");
// Validate ranges
ValidateRange(config.Port, 1, 65535, "Port");
ValidateRange(config.Database.MaxConnections, 1, 1000, "Database.MaxConnections");
ValidateRange(config.Api.RetryCount, 0, 10, "Api.RetryCount");
// Validate URLs
ValidateUrl(config.Api.BaseUrl, "Api.BaseUrl");
// Validate connection string format
ValidateConnectionString(config.Database.ConnectionString, "Database.ConnectionString");
return _errors.Count == 0;
}
private void ValidateRequired(string value, string fieldName)
{
if (string.IsNullOrWhiteSpace(value))
{
_errors.Add(new ValidationError(fieldName, "Value is required"));
}
}
private void ValidateRange(int value, int min, int max, string fieldName)
{
if (value < min || value > max)
{
_errors.Add(new ValidationError(fieldName,
$"Value must be between {min} and {max}"));
}
}
private void ValidateUrl(string url, string fieldName)
{
if (!Uri.TryCreate(url, UriKind.Absolute, out _))
{
_errors.Add(new ValidationError(fieldName, "Invalid URL format"));
}
}
private void ValidateConnectionString(string connectionString, string fieldName)
{
if (!string.IsNullOrEmpty(connectionString))
{
try
{
var builder = new SqlConnectionStringBuilder(connectionString);
// Basic validation passed
}
catch
{
_errors.Add(new ValidationError(fieldName, "Invalid connection string format"));
}
}
}
public IEnumerable<ValidationError> GetErrors()
{
return _errors.AsReadOnly();
}
}
public class ValidationError
{
public string FieldName { get; }
public string Message { get; }
public ValidationError(string fieldName, string message)
{
FieldName = fieldName;
Message = message;
}
public override string ToString()
{
return $"{FieldName}: {Message}";
}
}
Fluent Validation
public class ConfigurationFluentValidator
{
public static ValidationResult Validate(AppConfiguration config)
{
var validator = new InlineValidator<AppConfiguration>(); validator.RuleFor(x => x.AppName)
.NotEmpty()
.MaximumLength(100);
validator.RuleFor(x => x.Port)
.InclusiveBetween(1, 65535);
validator.RuleFor(x => x.Database.ConnectionString)
.NotEmpty()
.Must(BeValidConnectionString)
.WithMessage("Invalid database connection string");
validator.RuleFor(x => x.Database.MaxConnections)
.InclusiveBetween(1, 1000);
validator.RuleFor(x => x.Api.BaseUrl)
.NotEmpty()
.Must(BeValidUrl)
.WithMessage("Invalid API base URL");
validator.RuleFor(x => x.Api.ApiKey)
.NotEmpty()
.MinimumLength(10);
validator.RuleFor(x => x.Api.RetryCount)
.InclusiveBetween(0, 10);
return validator.Validate(config);
}
private static bool BeValidConnectionString(string connectionString)
{
if (string.IsNullOrEmpty(connectionString))
return false;
try
{
var builder = new SqlConnectionStringBuilder(connectionString);
return true;
}
catch
{
return false;
}
}
private static bool BeValidUrl(string url)
{
return Uri.TryCreate(url, UriKind.Absolute, out _);
}
}
Dynamic Configuration
Configuration Change Monitoring
public class DynamicConfiguration
{
private readonly FileSystemWatcher _fileWatcher;
private readonly TuskLang _tuskLang;
private readonly Action<AppConfiguration> _onConfigurationChanged;
private readonly Timer _reloadTimer; public DynamicConfiguration(string configPath, Action<AppConfiguration> onConfigurationChanged)
{
_tuskLang = new TuskLang(configPath);
_onConfigurationChanged = onConfigurationChanged;
_reloadTimer = new Timer(ReloadConfiguration, null, Timeout.Infinite, Timeout.Infinite);
// Watch for file changes
_fileWatcher = new FileSystemWatcher(Path.GetDirectoryName(configPath))
{
Filter = Path.GetFileName(configPath),
NotifyFilter = NotifyFilters.LastWrite | NotifyFilters.Size,
EnableRaisingEvents = true
};
_fileWatcher.Changed += OnConfigFileChanged;
}
private void OnConfigFileChanged(object sender, FileSystemEventArgs e)
{
// Debounce rapid file changes
_reloadTimer.Change(500, Timeout.Infinite);
}
private void ReloadConfiguration(object state)
{
try
{
var newConfig = LoadConfiguration();
_onConfigurationChanged?.Invoke(newConfig);
}
catch (Exception ex)
{
// Log error but don't crash
Console.WriteLine($"Error reloading configuration: {ex.Message}");
}
}
private AppConfiguration LoadConfiguration()
{
return new AppConfiguration
{
AppName = _tuskLang.GetValue<string>("app.name", "MyApp"),
Environment = _tuskLang.GetValue<string>("app.environment", "Development"),
Port = _tuskLang.GetValue<int>("app.port", 5000),
EnableLogging = _tuskLang.GetValue<bool>("app.enableLogging", true),
Database = new DatabaseConfig
{
ConnectionString = _tuskLang.GetValue<string>("database.connectionString", ""),
MaxConnections = _tuskLang.GetValue<int>("database.maxConnections", 100),
CommandTimeout = TimeSpan.FromSeconds(
_tuskLang.GetValue<int>("database.commandTimeoutSeconds", 30))
},
Api = new ApiConfig
{
BaseUrl = _tuskLang.GetValue<string>("api.baseUrl", ""),
ApiKey = _tuskLang.GetValue<string>("api.apiKey", ""),
RetryCount = _tuskLang.GetValue<int>("api.retryCount", 3),
Timeout = TimeSpan.FromSeconds(
_tuskLang.GetValue<int>("api.timeoutSeconds", 30))
}
};
}
public void Dispose()
{
_fileWatcher?.Dispose();
_reloadTimer?.Dispose();
_tuskLang?.Dispose();
}
}
Configuration Hot Reload
public class ConfigurationHotReload
{
private readonly IOptionsMonitor<AppConfiguration> _configMonitor;
private readonly ILogger<ConfigurationHotReload> _logger; public ConfigurationHotReload(IOptionsMonitor<AppConfiguration> configMonitor,
ILogger<ConfigurationHotReload> logger)
{
_configMonitor = configMonitor;
_logger = logger;
// Subscribe to configuration changes
_configMonitor.OnChange(OnConfigurationChanged);
}
private void OnConfigurationChanged(AppConfiguration newConfig, string name)
{
_logger.LogInformation("Configuration changed for {Name}", name);
// Handle configuration changes
HandleConfigurationChange(newConfig);
}
private void HandleConfigurationChange(AppConfiguration newConfig)
{
// Update application settings based on configuration changes
if (newConfig.EnableLogging)
{
_logger.LogInformation("Logging enabled");
}
else
{
_logger.LogInformation("Logging disabled");
}
// Update database connection if changed
if (!string.IsNullOrEmpty(newConfig.Database.ConnectionString))
{
_logger.LogInformation("Database connection string updated");
}
// Update API settings if changed
if (!string.IsNullOrEmpty(newConfig.Api.BaseUrl))
{
_logger.LogInformation("API base URL updated to {BaseUrl}", newConfig.Api.BaseUrl);
}
}
public AppConfiguration CurrentConfiguration => _configMonitor.CurrentValue;
}
Secret Management
Secret Configuration
public class SecretConfiguration
{
private readonly TuskLang _tuskLang;
private readonly IEncryptionService _encryptionService; public SecretConfiguration(TuskLang tuskLang, IEncryptionService encryptionService)
{
_tuskLang = tuskLang;
_encryptionService = encryptionService;
}
public string GetSecret(string key, string defaultValue = "")
{
var encryptedValue = _tuskLang.GetValue<string>($"secrets.{key}", defaultValue);
if (string.IsNullOrEmpty(encryptedValue) || encryptedValue == defaultValue)
return defaultValue;
try
{
return _encryptionService.Decrypt(encryptedValue);
}
catch
{
return defaultValue;
}
}
public void SetSecret(string key, string value)
{
var encryptedValue = _encryptionService.Encrypt(value);
_tuskLang.SetValue($"secrets.{key}", encryptedValue);
}
public Dictionary<string, string> GetAllSecrets()
{
var secrets = new Dictionary<string, string>();
var allValues = _tuskLang.GetAllValues();
foreach (var (key, value) in allValues)
{
if (key.StartsWith("secrets."))
{
var secretKey = key.Substring("secrets.".Length);
var decryptedValue = GetSecret(secretKey);
secrets[secretKey] = decryptedValue;
}
}
return secrets;
}
}
public interface IEncryptionService
{
string Encrypt(string plainText);
string Decrypt(string cipherText);
}
public class AesEncryptionService : IEncryptionService
{
private readonly byte[] _key;
private readonly byte[] _iv;
public AesEncryptionService(string key, string iv)
{
_key = Convert.FromBase64String(key);
_iv = Convert.FromBase64String(iv);
}
public string Encrypt(string plainText)
{
using var aes = Aes.Create();
aes.Key = _key;
aes.IV = _iv;
using var encryptor = aes.CreateEncryptor();
var plainBytes = Encoding.UTF8.GetBytes(plainText);
var cipherBytes = encryptor.TransformFinalBlock(plainBytes, 0, plainBytes.Length);
return Convert.ToBase64String(cipherBytes);
}
public string Decrypt(string cipherText)
{
using var aes = Aes.Create();
aes.Key = _key;
aes.IV = _iv;
using var decryptor = aes.CreateDecryptor();
var cipherBytes = Convert.FromBase64String(cipherText);
var plainBytes = decryptor.TransformFinalBlock(cipherBytes, 0, cipherBytes.Length);
return Encoding.UTF8.GetString(plainBytes);
}
}
Configuration Providers
Custom Configuration Provider
public class TuskLangConfigurationProvider : ConfigurationProvider
{
private readonly string _filePath;
private readonly FileSystemWatcher _fileWatcher; public TuskLangConfigurationProvider(string filePath)
{
_filePath = filePath;
if (File.Exists(filePath))
{
_fileWatcher = new FileSystemWatcher(Path.GetDirectoryName(filePath))
{
Filter = Path.GetFileName(filePath),
NotifyFilter = NotifyFilters.LastWrite | NotifyFilters.Size,
EnableRaisingEvents = true
};
_fileWatcher.Changed += OnConfigFileChanged;
}
}
public override void Load()
{
if (!File.Exists(_filePath))
return;
try
{
var tuskLang = new TuskLang(_filePath);
var config = tuskLang.GetAllValues();
Data.Clear();
foreach (var (key, value) in config)
{
Data[key] = value?.ToString();
}
}
catch (Exception ex)
{
// Log error but don't crash
Console.WriteLine($"Error loading TuskLang configuration: {ex.Message}");
}
}
private void OnConfigFileChanged(object sender, FileSystemEventArgs e)
{
// Reload configuration when file changes
Load();
OnReload();
}
protected override void Dispose(bool disposing)
{
_fileWatcher?.Dispose();
base.Dispose(disposing);
}
}
public class TuskLangConfigurationSource : IConfigurationSource
{
private readonly string _filePath;
public TuskLangConfigurationSource(string filePath)
{
_filePath = filePath;
}
public IConfigurationProvider Build(IConfigurationBuilder builder)
{
return new TuskLangConfigurationProvider(_filePath);
}
}
public static class ConfigurationBuilderExtensions
{
public static IConfigurationBuilder AddTuskLangFile(
this IConfigurationBuilder builder, string path)
{
return builder.Add(new TuskLangConfigurationSource(path));
}
}
TuskLang Integration
TuskLang Configuration Service
public class TuskLangConfigurationService
{
private readonly TuskLang _tuskLang;
private readonly ILogger<TuskLangConfigurationService> _logger; public TuskLangConfigurationService(TuskLang tuskLang,
ILogger<TuskLangConfigurationService> logger)
{
_tuskLang = tuskLang;
_logger = logger;
}
public T GetValue<T>(string key, T defaultValue = default)
{
try
{
return _tuskLang.GetValue(key, defaultValue);
}
catch (Exception ex)
{
_logger.LogWarning(ex, "Error getting configuration value for key {Key}", key);
return defaultValue;
}
}
public void SetValue<T>(string key, T value)
{
try
{
_tuskLang.SetValue(key, value);
_logger.LogDebug("Configuration value set for key {Key}", key);
}
catch (Exception ex)
{
_logger.LogError(ex, "Error setting configuration value for key {Key}", key);
throw;
}
}
public Dictionary<string, object> GetAllValues()
{
try
{
return _tuskLang.GetAllValues();
}
catch (Exception ex)
{
_logger.LogError(ex, "Error getting all configuration values");
return new Dictionary<string, object>();
}
}
public bool HasValue(string key)
{
try
{
return _tuskLang.HasValue(key);
}
catch (Exception ex)
{
_logger.LogWarning(ex, "Error checking if configuration key {Key} exists", key);
return false;
}
}
public void Reload()
{
try
{
_tuskLang.Reload();
_logger.LogInformation("Configuration reloaded successfully");
}
catch (Exception ex)
{
_logger.LogError(ex, "Error reloading configuration");
throw;
}
}
}
Configuration Dependency Injection
public static class ServiceCollectionExtensions
{
public static IServiceCollection AddTuskLangConfiguration(
this IServiceCollection services, string configPath)
{
// Register TuskLang instance
services.AddSingleton<TuskLang>(provider => new TuskLang(configPath)); // Register configuration service
services.AddSingleton<TuskLangConfigurationService>();
// Register configuration objects
services.AddSingleton<AppConfiguration>(provider =>
{
var configService = provider.GetRequiredService<TuskLangConfigurationService>();
return new AppConfiguration
{
AppName = configService.GetValue<string>("app.name", "MyApp"),
Environment = configService.GetValue<string>("app.environment", "Development"),
Port = configService.GetValue<int>("app.port", 5000),
EnableLogging = configService.GetValue<bool>("app.enableLogging", true),
Database = new DatabaseConfig
{
ConnectionString = configService.GetValue<string>("database.connectionString", ""),
MaxConnections = configService.GetValue<int>("database.maxConnections", 100),
CommandTimeout = TimeSpan.FromSeconds(
configService.GetValue<int>("database.commandTimeoutSeconds", 30))
},
Api = new ApiConfig
{
BaseUrl = configService.GetValue<string>("api.baseUrl", ""),
ApiKey = configService.GetValue<string>("api.apiKey", ""),
RetryCount = configService.GetValue<int>("api.retryCount", 3),
Timeout = TimeSpan.FromSeconds(
configService.GetValue<int>("api.timeoutSeconds", 30))
}
};
});
return services;
}
}
Best Practices
Configuration Best Practices
public class ConfigurationBestPractices
{
private readonly ILogger<ConfigurationBestPractices> _logger; public ConfigurationBestPractices(ILogger<ConfigurationBestPractices> logger)
{
_logger = logger;
}
// ✅ Good: Use strongly-typed configuration
public void UseStronglyTypedConfig(AppConfiguration config)
{
var port = config.Port; // Type-safe access
var connectionString = config.Database.ConnectionString;
}
// ❌ Bad: Use string-based configuration
public void UseStringBasedConfig(IConfiguration configuration)
{
var port = int.Parse(configuration["Port"]); // Error-prone
var connectionString = configuration["Database:ConnectionString"];
}
// ✅ Good: Validate configuration on startup
public void ValidateOnStartup(AppConfiguration config)
{
var validator = new ConfigurationValidator();
if (!validator.ValidateConfiguration(config))
{
var errors = validator.GetErrors();
foreach (var error in errors)
{
_logger.LogError("Configuration error: {Error}", error);
}
throw new InvalidOperationException("Invalid configuration");
}
}
// ✅ Good: Use environment-specific configuration
public AppConfiguration LoadEnvironmentConfig(string environment)
{
var configPath = $"config/{environment}.tsk";
if (!File.Exists(configPath))
{
throw new FileNotFoundException($"Configuration file not found: {configPath}");
}
return EnvironmentSettings.GetConfiguration(environment);
}
// ✅ Good: Handle configuration changes gracefully
public void HandleConfigChanges(AppConfiguration newConfig)
{
try
{
// Validate new configuration
var validator = new ConfigurationValidator();
if (!validator.ValidateConfiguration(newConfig))
{
_logger.LogWarning("Invalid configuration change, keeping current config");
return;
}
// Apply configuration changes
ApplyConfigurationChanges(newConfig);
_logger.LogInformation("Configuration updated successfully");
}
catch (Exception ex)
{
_logger.LogError(ex, "Error applying configuration changes");
}
}
private void ApplyConfigurationChanges(AppConfiguration newConfig)
{
// Apply changes to running application
// This could involve restarting services, updating connections, etc.
}
}
Security Best Practices
public class ConfigurationSecurity
{
private readonly IEncryptionService _encryptionService;
private readonly ILogger<ConfigurationSecurity> _logger; public ConfigurationSecurity(IEncryptionService encryptionService,
ILogger<ConfigurationSecurity> logger)
{
_encryptionService = encryptionService;
_logger = logger;
}
// ✅ Good: Encrypt sensitive configuration
public void StoreSensitiveConfig(string key, string value)
{
var encryptedValue = _encryptionService.Encrypt(value);
// Store encrypted value in configuration
}
// ✅ Good: Use secure configuration providers
public void UseSecureProvider()
{
// Use Azure Key Vault, AWS Secrets Manager, or similar
// for production secrets
}
// ✅ Good: Validate configuration permissions
public void ValidateConfigPermissions(string configPath)
{
var fileInfo = new FileInfo(configPath);
var permissions = fileInfo.Attributes;
if ((permissions & FileAttributes.ReadOnly) == 0)
{
_logger.LogWarning("Configuration file is not read-only");
}
}
// ✅ Good: Audit configuration access
public void AuditConfigAccess(string key, string user)
{
_logger.LogInformation("Configuration accessed - Key: {Key}, User: {User}", key, user);
}
}
Summary
This comprehensive configuration management guide covers:
- Basic Configuration: Simple configuration setup and loading - Hierarchical Configuration: Multi-level configuration with precedence - Environment-Specific Configuration: Different settings for different environments - Configuration Validation: Ensuring configuration correctness - Dynamic Configuration: Hot reloading and change monitoring - Secret Management: Secure handling of sensitive configuration - Configuration Providers: Custom providers for TuskLang integration - TuskLang Integration: Deep integration with TuskLang features - Best Practices: Security, validation, and maintainability guidelines
The patterns ensure robust, secure, and maintainable configuration management in C# applications using TuskLang.