Extends .NET Options pattern to support writing back to IConfiguration while mutating strongly-typed options. Commonly Used Types: Microsoft.Extensions.Options.Mutable.IOptionsMutator Microsoft.Extensions.Options.Mutable.IMutableOptionsMonitor
$ dotnet add package MutableOptionsExtends .NET Options pattern to support writing back to IConfiguration while mutating strongly-typed options.
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> to modify the configuration: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
IOptions<T>, IOptionsSnapshot<T>, IOptionsMonitor<T>, IConfigureOptions etc.name parameter to IOptionsMutator.Mutate method to change value of named options.BinderOptions where it could be specified whether it can detect changed in non-public properties and white them to IConfiguration.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 });