Extends .NET Options pattern to support writing back to IConfiguration while mutating strongly-typed options.
Register in dependency injection service container
ConfigureMutable
method is provided, that basically can be used instead of Configure
for options that needs to support mutations:
services.ConfigureMutable<SimpleOptions>(configurationRoot.GetSection("SimpleOptions"));
IOptionsMutator<SimpleOptions> optionsMutator
optionsMutator.Mutate(options => options with { Bar = 42 });
For convenience in situations when same code have to read and write options there is IMutableOptionsMonitor<TOptions> : IOptionsMonitor<TOptions>, IOptionsMutator<TOptions>
interface.
Both IOptionsMutator<TOptions>
and IMutableOptionsMonitor<TOptions>
are registered with singleton lifetime.
Install NuGet package from Package Manager Console:
PM> Install-Package MutableOptions
- Compatible with all standartd Options patter interfaces, e.g.
IOptions<T>
,IOptionsSnapshot<T>
,IOptionsMonitor<T>
, IConfigureOptions etc. - Named options are supported. Specify
name
parameter toIOptionsMutator.Mutate
method to change value of named options. - Supports
BinderOptions
where it could be specified whether it can detect changed in non-public properties and write them toIConfiguration
. - Can write values of most primitive types properties in flat options types.
NOTE: Complex hierarchical options types, that contain nested classes or collections, is not supported!
IOptionsMutator is a core interface that allows changing configuration by mutating strongly typed options:
public interface IOptionsMutator<TOptions> where TOptions : class
{
bool Mutate(string? name, Func<TOptions, TOptions> mutator);
}
, where mutator
func should return new TOptions
instance based on TOptions
instance provided, changing the values of some or all properties.
It is recommended to define options types as immutable types, so it would not be possible to change state of TOptions
instance directly.
The easiest way to achieve that is to use records with init-only properties, e.g.:
public record SimpleOptions
{
public string Foo { get; init; } = string.Empty;
public int Bar { get; init; }
}
Record types also implement IEquatable<T>
, which allows to configure mutable options without specifying custom IEqualityComparer<T>
.
Another usefult capability of record types is that they have support for with
expressions to enable non-destructive mutation of records, which can be used to simplify mutator
function implementtion.
optionsMutator.Mutate(options => options with { Bar = 42 });