Example 04: Dynamic Settings Configuration System
Overview
The dashboard features a comprehensive settings management system that allows dynamic configuration at multiple scopes (Program, Company, Operator) without requiring code changes or redeployment.
MepSettings Implementation
Settings Definition (MepSettings.json)
[
{
"SettingName": "OrderByWarehouse",
"SettingValue": "true",
"SettingScope": "Company",
"DisplayName": "Group Orders by Warehouse",
"Description": "When enabled, orders are grouped by warehouse",
"DataType": "Boolean",
"Category": "Ordering"
},
{
"SettingName": "MaxOrderLines",
"SettingValue": "100",
"SettingScope": "Program",
"DisplayName": "Maximum Order Lines",
"Description": "Maximum lines per purchase order",
"DataType": "Integer",
"MinValue": "1",
"MaxValue": "999",
"Category": "Limits"
},
{
"SettingName": "DefaultBuyer",
"SettingValue": "",
"SettingScope": "Operator",
"DisplayName": "Default Buyer Code",
"Description": "Default buyer for new orders",
"DataType": "String",
"ListQuery": "SELECT Buyer as Value, Name as Display FROM InvBuyer",
"Category": "Defaults"
}
]
Settings Service
public class MepSettingsService : IMepSettingsService
{
public async Task InitializeDefaultAsync()
{
// Load settings from JSON
var settingsJson = GetResourceFile("MepSettings.json");
Settings = JsonConvert.DeserializeObject<List<MepSettings>>(settingsJson);
foreach (var setting in Settings)
{
// Check current value by scope
string currentValue = await GetValueByScope(
setting.SettingName,
setting.SettingScope);
if (string.IsNullOrEmpty(currentValue))
{
// Set default value
await SetValueByScope(
setting.SettingName,
setting.SettingValue,
setting.SettingScope);
}
else
{
setting.SettingValue = currentValue;
}
}
}
private async Task<string> GetValueByScope(
string name, string scope)
{
switch (scope.ToUpper())
{
case "OPERATOR":
return await _preferenceManager.GetByOperatorAsync(name);
case "COMPANY":
return await _preferenceManager.GetByCompanyAsync(name);
case "COMPANYOPERATOR":
return await _preferenceManager.GetByCompanyOperatorAsync(name);
default:
return await _preferenceManager.GetByProgramAsync(name);
}
}
}
Settings UI Dialog
public class SettingsDialogViewModel : BaseViewModel
{
public ObservableCollection<SettingViewModel> Settings { get; }
public async Task LoadSettingsAsync()
{
var settings = _mepSettingsService.Settings;
foreach (var setting in settings)
{
var vm = new SettingViewModel(setting);
// Load list values if needed
if (!string.IsNullOrEmpty(setting.ListQuery))
{
vm.ListValues = await LoadListValues(setting.ListQuery);
}
Settings.Add(vm);
}
}
public async Task SaveSettingsAsync()
{
foreach (var setting in Settings.Where(s => s.IsModified))
{
await SaveSettingByScope(
setting.Name,
setting.Value,
setting.Scope);
}
}
}
Dynamic Behavior
Configuration-Driven Logic
// Order grouping based on settings
var orderSummaries = _invOrderingService.CreateOrderSummary(
SelectedItems,
SelectedBuyer.Buyer,
_mepSettingsService.GetBool("OrderByWarehouse"),
_mepSettingsService.GetBool("OrderByDueDate"));
// Dynamic limits
var maxLines = _mepSettingsService.GetInt("MaxOrderLines", 100);
if (orderLines.Count > maxLines)
{
// Split into multiple orders
}
Validation Rules
public bool ValidateSetting(MepSettings setting, string value)
{
switch (setting.DataType)
{
case "Integer":
if (!int.TryParse(value, out var intVal))
return false;
if (setting.MinValue.HasValue && intVal < setting.MinValue)
return false;
if (setting.MaxValue.HasValue && intVal > setting.MaxValue)
return false;
return true;
case "Boolean":
return bool.TryParse(value, out _);
case "String":
if (setting.MaxLength.HasValue && value.Length > setting.MaxLength)
return false;
if (!string.IsNullOrEmpty(setting.ValidationRegex))
return Regex.IsMatch(value, setting.ValidationRegex);
return true;
}
}
Benefits
- No code changes for configuration
- User-specific preferences
- Company-wide settings
- Runtime configuration changes
- Validation and constraints
- Audit trail of changes