Skip to main content

Settings

Overview​

Ramsha Framework provides a powerful, extensible, and strongly-typed settings system designed to centralize application configuration while keeping it explicit, predictable, and scalable.

Ramsha settings:

  • Are defined centrally
  • Are strongly typed
  • Support default values
  • Are grouped logically
  • Can be resolved from multiple sources
  • Are fully extensible via custom resolvers
  • Allow fine-grained control over resolution order

Core Concepts​

What Is a Setting?​

A setting represents a configurable value that affects application behavior, such as:

  • Business rules
  • Feature toggles
  • Thresholds and limits
  • Application-level options

Defining Settings Models​

Settings values are represented using plain C# classes.

Example: Product Discount Settings​

public class ProductDiscountSettings
{
public int Discount { get; set; }
public bool HasDiscount { get; set; }
}

This ensures:

  • Strong typing
  • IntelliSense support
  • Compile-time safety

Defining Setting Names​

Setting names should always be centralized to avoid magic strings.

public class ProductSettingNames
{
public const string GroupName = "ProductsSettings";
public const string DiscountSettings = "DiscountSettings";
}

Setting Definitions​

Internally, every setting is represented by a SettingDefinition.

public class SettingDefinition
{
public string Name { get; set; }
public string? GroupName { get; set; }
public Type ValueType { get; set; }
public string? Description { get; set; }
public object? DefaultValue { get; set; }
public List<string> Providers { get; set; } = [];
}

Explanation​

PropertyDescription
NameUnique name of the setting
GroupNameOptional group name
ValueTypeCLR type of the value
DescriptionOptional description
DefaultValueFallback value
ProvidersAllowed resolver provider names

Creating Setting Definition Providers​

Settings are defined using Setting Definition Providers.

public class ProductSettingDefinitions : ISettingDefinitionProvider
{
public void Define(ISettingDefinitionContext context)
{
context.Group(ProductSettingNames.GroupName, group =>
{
group.Setting(
ProductSettingNames.DiscountSettings,
new ProductDiscountSettings
{
Discount = 20,
HasDiscount = true
});
});
}
}

What This Does​

  • Creates a settings group
  • Defines a setting
  • Assigns a strongly typed default value

Registering Setting Definitions​

Setting definitions must be registered during the BuildServices phase of a module.

public override void BuildServices(BuildServicesContext context)
{
context.Services.Configure<RamshaSettingsOptions>(options =>
{
options.DefinitionProviders.Add<ProductSettingDefinitions>();
});
}

Setting Value Resolvers​

Settings values are resolved using Setting Value Resolvers, which determine where a setting value comes from, such as:

  • Configuration
  • Memory defaults
  • Database
  • External systems

All resolvers inherit from:

SettingValueResolver

Each resolver has a provider name, which uniquely identifies it.


Built-in Resolvers​

Configuration Setting Resolver​

public class ConfigurationSettingValueResolver(IConfiguration configuration)
: SettingValueResolver("C")
{
public override Task<T?> ResolveAsync<T>(SettingDefinition def)
where T : default
{
var section = def.GroupName is not null
? configuration.GetSection(def.GroupName).GetSection(def.Name)
: configuration.GetSection(def.Name);

return Task.FromResult(section.Get<T>());
}
}

Provider Name: C Reads values from configuration sources.


Memory (Default) Setting Resolver​

public class MemorySettingValueResolver()
: SettingValueResolver("M")
{
public override Task<T?> ResolveAsync<T>(SettingDefinition def)
where T : default
{
return Task.FromResult(
def.DefaultValue is not null
? (T)def.DefaultValue
: default
);
}
}

Provider Name: M Behavior: returns the default value defined in the SettingDefinition.


Registering Setting Value Resolvers​

Resolvers are registered in RamshaSettingsOptions:

services.Configure<RamshaSettingsOptions>(options =>
{
options.ValueResolvers.Add<ConfigurationSettingValueResolver>();
options.ValueResolvers.Add<MemorySettingValueResolver>();
});

Resolver Order Matters​

  • Resolvers are executed in the order they appear.
  • The first resolver that returns a value wins.
  • You can control the order using:
options.ValueResolvers.AddBefore<MemorySettingValueResolver, CustomResolver>();
options.ValueResolvers.AddAfter<ConfigurationSettingValueResolver, CustomResolver>();

Custom Resolvers​

You can create custom resolvers by inheriting from SettingValueResolver.

public class CustomSettingResolver : SettingValueResolver("X")
{
public override Task<T?> ResolveAsync<T>(SettingDefinition def)
where T : default
{
// custom logic
return Task.FromResult(default(T));
}
}

Then register it:

services.Configure<RamshaSettingsOptions>(options =>
{
options.ValueResolvers.Add<CustomSettingResolver>();
});

Provider-Based Resolution​

Each SettingDefinition can specify allowed provider names:

  • If Providers is empty: all resolvers are allowed
  • If Providers contains provider names: only resolvers with matching names execute
group.Setting(
ProductSettingNames.DiscountSettings,
new ProductDiscountSettings { Discount = 20, HasDiscount = true },
providers: new[] { "C" } // only configuration resolver
);

Resolving Settings Using ISettingResolver​

Settings are consumed via ISettingResolver:

public class AppSettingsController(
ISettingResolver settingResolver
) : RamshaApiController
{
[HttpGet(nameof(GetProductDiscountSettings))]
public async Task<ProductDiscountSettings?> GetProductDiscountSettings()
{
return await settingResolver.ResolveAsync<ProductDiscountSettings>(
ProductSettingNames.DiscountSettings
);
}
}
  • Finds the setting definition
  • Applies provider filtering
  • Executes resolvers in order
  • Returns strongly typed value

Summary​

Ramsha Settings System:

  • Centralizes settings definitions
  • Supports multiple value sources via resolvers
  • Provides fine-grained control over resolver order
  • Allows custom resolvers
  • Supports provider-based filtering
  • Provides safe defaults
  • Ensures explicit, predictable, and strongly typed configuration

This design makes your configuration:

  • Clear
  • Maintainable
  • Flexible
  • Fully aligned with Clean Architecture principles

Next Steps​