Comprehensive configuration management library for .NET with 8 unique features: Property-Level Change Tracking, Configuration Snapshots, Persistent Storage (File/Database), Simple Key-Value Extensions, Environment Overrides, Change History, Auto-Validation, and Type-Safe Access. Perfect for building robust applications with auditable configuration management.
$ dotnet add package SkyWebFramework.ConfigurationA powerful, feature-rich .NET configuration library that simplifies configuration management with validation, type safety, change tracking, and persistence capabilities.
dotnet add package SkyWebFramework.Configuration
Or via NuGet Package Manager:
Install-Package SkyWebFramework.Configuration
// Configuration class
public class AppSettings
{
[Required]
public string ApplicationName { get; set; }
[Range(1, 100)]
public int MaxConnections { get; set; }
[EmailAddress]
public string AdminEmail { get; set; }
}
// In appsettings.json
{
"AppSettings": {
"ApplicationName": "MyApp",
"MaxConnections": 50,
"AdminEmail": "admin@example.com"
}
}
// Register in Startup/Program.cs
builder.Services.AddEasyConfig<AppSettings>(builder.Configuration);
// Use in your code
public class MyService
{
private readonly AppSettings _settings;
public MyService(AppSettings settings)
{
_settings = settings;
}
}
// Get required values
var apiKey = configuration.GetRequiredValue<string>("ApiKey");
var timeout = configuration.GetRequiredValue<int>("Timeout");
// Get with defaults
var retryCount = configuration.GetValueOrDefault("RetryCount", 3);
var isEnabled = configuration.GetBool("FeatureFlag", defaultValue: true);
// Get validated values
var port = configuration.GetValidatedValue("Port",
value => value > 0 && value < 65536,
"Port must be between 1 and 65535");
// Connection strings
var connString = configuration.GetRequiredConnectionString("DefaultConnection");
// Boolean parsing (supports: true/false, yes/no, 1/0, on/off, enabled/disabled)
var isDebug = configuration.GetBool("Debug");
// Integer with validation
var maxSize = configuration.GetInt("MaxSize", defaultValue: 100, minValue: 1, maxValue: 1000);
// Enums
var logLevel = configuration.GetEnum<LogLevel>("LogLevel", LogLevel.Information);
// TimeSpan
var timeout = configuration.GetTimeSpan("Timeout", TimeSpan.FromSeconds(30));
// URI
var apiUrl = configuration.GetRequiredUri("ApiEndpoint");
// Arrays and Lists
var allowedHosts = configuration.GetStringArray("AllowedHosts", separator: ";");
var ports = configuration.GetList<int>("Ports");
builder.Services.AddEasyConfig<AppSettings>(
builder.Configuration,
sectionName: "MyApp",
options =>
{
options.Validate = true; // Enable validation
options.RequireSection = true; // Throw if section missing
options.ReloadOnChange = true; // Auto-reload on file changes
options.EnableChangeTracking = true; // Track property changes
options.ChangeHistorySize = 50; // Keep 50 changes in history
options.AutoCreateSection = false; // Don't auto-create missing sections
options.EnvironmentSuffix = "Development"; // Use "MyApp:Development" section
options.Lifetime = ServiceLifetime.Singleton; // Service lifetime
});
// Register config with change tracking
builder.Services.AddEasyConfig<AppSettings>(
builder.Configuration,
options =>
{
options.ReloadOnChange = true;
options.EnableChangeTracking = true;
});
// Subscribe to changes
public class ConfigMonitorService
{
public ConfigMonitorService(IConfigChangeManager<AppSettings> changeManager)
{
// Track specific property changes
changeManager.OnPropertyChange("MaxConnections", change =>
{
Console.WriteLine($"MaxConnections changed from {change.OldValue} to {change.NewValue}");
});
// Track all changes
changeManager.OnAnyChange(change =>
{
Console.WriteLine($"{change.PropertyName} changed at {change.ChangedAt}");
});
// Get change history
var history = changeManager.GetChangeHistory("MaxConnections", count: 10);
}
}
// Create a snapshot
var snapshot = configuration.CreateSnapshot<AppSettings>("before-deployment");
Console.WriteLine($"Snapshot created at {snapshot.CapturedAt}");
// Compare with current config
var current = configuration.GetEasyConfig<AppSettings>();
// ... compare snapshot.Value with current ...
builder.Services.AddPersistentConfig<AppSettings>(
builder.Configuration,
options =>
{
options.EnablePersistence = true;
options.PersistencePath = "config-history"; // Optional: custom path
options.AutoPersistChanges = true;
options.ReloadOnChange = true;
options.EnableChangeTracking = true;
});
// Add DbContext
builder.Services.AddDbContext<ConfigDbContext>(options =>
options.UseSqlServer(connectionString));
// Register with database persistence
builder.Services.AddPersistentConfig<AppSettings>(
builder.Configuration,
options =>
{
options.EnablePersistence = true;
options.PersistenceStore = new DatabasePersistenceStore(serviceProvider);
options.AutoPersistChanges = true;
});
// Query historical changes
public class ConfigAuditService
{
private readonly IPersistentConfigChangeManager<AppSettings> _changeManager;
public async Task<IEnumerable<ConfigChangeInfo<AppSettings>>> GetAuditTrail()
{
return await _changeManager.GetPersistedChangesAsync(
"MaxConnections",
startDate: DateTime.UtcNow.AddDays(-30),
maxResults: 100
);
}
}
// appsettings.json
{
"AppSettings": {
"ApiUrl": "https://api.example.com",
"Development": {
"ApiUrl": "https://dev-api.example.com"
},
"Production": {
"ApiUrl": "https://prod-api.example.com"
}
}
}
// Register with environment override
builder.Services.AddEasyConfig<AppSettings>(
builder.Configuration,
options =>
{
options.EnvironmentSuffix = builder.Environment.EnvironmentName;
});
builder.Services.AddEasyConfig<AppSettings>(
builder.Configuration,
options =>
{
options.CustomValidator = config =>
{
var settings = (AppSettings)config;
if (settings.MaxConnections > 100 && !settings.IsPremium)
{
throw new InvalidOperationException(
"MaxConnections over 100 requires premium subscription");
}
};
});
| Method | Description |
|---|---|
AddEasyConfig<T>() | Register configuration with full options |
TryAddEasyConfig<T>() | Register only if not already registered |
GetEasyConfig<T>() | Get configuration instance directly |
CreateSnapshot<T>() | Create configuration snapshot |
ValidateEasyConfigs() | Validate multiple configurations |
| Method | Description |
|---|---|
GetRequiredValue<T>() | Get value or throw |
GetValueOrDefault<T>() | Get value with default fallback |
GetValidatedValue<T>() | Get and validate value |
GetRequiredConnectionString() | Get connection string or throw |
GetBool() | Parse boolean with common formats |
GetInt() | Parse integer with min/max validation |
GetDouble() | Parse double with min/max validation |
GetTimeSpan() | Parse TimeSpan |
GetEnum<T>() | Parse enum value |
GetUri() | Parse URI with validation |
GetRequiredUri() | Parse required URI |
GetStringArray() | Split string to array |
GetList<T>() | Split and convert to typed list |
GetSectionAsDictionary() | Get section as dictionary |
HasValue() | Check if key exists |
| Method | Description |
|---|---|
AddPersistentConfig<T>() | Register with persistent storage |
CreateAndPersistSnapshot<T>() | Create and save snapshot |
GetPersistedSnapshot<T>() | Retrieve saved snapshot |
try
{
var config = configuration.GetEasyConfig<AppSettings>();
}
catch (ConfigValidationException ex)
{
Console.WriteLine($"Validation failed for {ex.Section}");
foreach (var error in ex.ValidationErrors)
{
Console.WriteLine($" - {error.ErrorMessage}");
}
}
catch (ConfigSectionMissingException ex)
{
Console.WriteLine($"Missing section: {ex.Section}");
}
public class ConfigChangeInfo<T>
{
public string PropertyName { get; set; }
public object? OldValue { get; set; }
public object? NewValue { get; set; }
public DateTime ChangedAt { get; set; }
public T CurrentConfig { get; set; }
}
public interface IConfigSnapshot<T>
{
T Value { get; }
DateTime CapturedAt { get; }
string Label { get; }
}
using SkyWebFramework.Configuration;
var builder = WebApplication.CreateBuilder(args);
// 1. Simple registration
builder.Services.AddEasyConfig<DatabaseSettings>(builder.Configuration);
// 2. Advanced registration with all features
builder.Services.AddPersistentConfig<AppSettings>(
builder.Configuration,
options =>
{
options.Validate = true;
options.ReloadOnChange = true;
options.EnableChangeTracking = true;
options.EnablePersistence = true;
options.ChangeHistorySize = 100;
options.EnvironmentSuffix = builder.Environment.EnvironmentName;
options.CustomValidator = config =>
{
var settings = (AppSettings)config;
// Custom validation logic
};
});
var app = builder.Build();
// 3. Use in controllers/services
public class MyController : ControllerBase
{
private readonly AppSettings _settings;
private readonly IConfigChangeManager<AppSettings> _changeManager;
public MyController(
AppSettings settings,
IConfigChangeManager<AppSettings> changeManager)
{
_settings = settings;
_changeManager = changeManager;
// Subscribe to changes
_changeManager.OnPropertyChange("ApiUrl", change =>
{
_logger.LogInformation(
"API URL changed from {Old} to {New}",
change.OldValue,
change.NewValue);
});
}
[HttpGet("config")]
public IActionResult GetConfig()
{
return Ok(new
{
Current = _settings,
History = _changeManager.GetChangeHistory("ApiUrl", 10)
});
}
}
public class ApiSettings
{
[Required]
[Url]
public string BaseUrl { get; set; }
[Required]
[Range(1, 3600)]
public int TimeoutSeconds { get; set; }
[Required]
[RegularExpression(@"^[a-zA-Z0-9-]+$")]
public string ApiKey { get; set; }
}
// Instead of one large class
public class EmailSettings { }
public class CacheSettings { }
public class LoggingSettings { }
// Register separately
services.AddEasyConfig<EmailSettings>(configuration);
services.AddEasyConfig<CacheSettings>(configuration);
services.AddEasyConfig<LoggingSettings>(configuration);
{
"Email": {
"SmtpServer": "smtp.example.com",
"Development": {
"SmtpServer": "localhost"
},
"Production": {
"SmtpServer": "smtp.production.com"
}
}
}
changeManager.OnPropertyChange("DatabaseConnectionString", change =>
{
_logger.LogWarning(
"⚠️ Database connection changed! Old: {Old}, New: {New}",
MaskConnectionString(change.OldValue?.ToString()),
MaskConnectionString(change.NewValue?.ToString()));
// Optionally trigger reconnection logic
_dbContext.Database.CloseConnection();
});
// In deployment script
var snapshot = await configuration.CreateAndPersistSnapshot<AppSettings>(
$"pre-deployment-{DateTime.UtcNow:yyyy-MM-dd-HH-mm}",
persistenceStore);
// Verify section exists
var section = configuration.GetSection("AppSettings");
if (!section.Exists())
{
Console.WriteLine("Section does not exist!");
}
// Check for case sensitivity
// "appsettings" vs "AppSettings"
try
{
services.AddEasyConfig<AppSettings>(configuration);
}
catch (ConfigValidationException ex)
{
foreach (var error in ex.ValidationErrors)
{
Console.WriteLine($"{error.MemberNames.First()}: {error.ErrorMessage}");
}
}
// Ensure ReloadOnChange is enabled
options.ReloadOnChange = true;
options.EnableChangeTracking = true;
// File must exist in config sources
builder.Configuration.AddJsonFile("appsettings.json",
optional: false,
reloadOnChange: true);
Contributions are welcome! Please feel free to submit a Pull Request.
This project is licensed under the MIT License - see the LICENSE file for details.
Made with ❤️ by the SkyWebFramework Team