Skip to main content

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