APIs for dynamically configuring options based on a given context.
From the command-line:
dotnet add package Microsoft.Extensions.Options.ContextualOr directly in the C# project file:
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Options.Contextual" Version="[CURRENTVERSION]" />
</ItemGroup>Start with an option type.
internal class WeatherForecastOptions
{
public string TemperatureScale { get; set; } = "Celsius"; // Celsius or Fahrenheit
public int ForecastDays { get; set; }
}Define a context and a receiver that will be used as inputs to dynamically configure the options.
[OptionsContext]
internal partial class WeatherForecastContext // Note class must be partial
{
public Guid UserId { get; set; }
public string? Country { get; set; }
}
internal class CountryContextReceiver : IOptionsContextReceiver
{
public string? Country { get; private set; }
public void Receive<T>(string key, T value)
{
if (key == nameof(Country))
{
Country = value?.ToString();
}
}
}Create a service that consumes the options for a given context.
internal class WeatherForecast
{
public DateTime Date { get; set; }
public int Temperature { get; set; }
public string TemperatureScale { get; set; } = string.Empty;
}
internal class WeatherForecastService
{
private readonly IContextualOptions<WeatherForecastOptions> _contextualOptions;
private readonly Random _rng = new(0);
public WeatherForecastService(IContextualOptions<WeatherForecastOptions> contextualOptions)
{
_contextualOptions = contextualOptions;
}
public async Task<IEnumerable<WeatherForecast>> GetForecast(WeatherForecastContext context, CancellationToken cancellationToken)
{
WeatherForecastOptions options = await _contextualOptions.GetAsync(context, cancellationToken).ConfigureAwait(false);
return Enumerable.Range(1, options.ForecastDays).Select(index => new WeatherForecast
{
Date = new DateTime(2000, 1, 1).AddDays(index),
Temperature = _rng.Next(-20, 55),
TemperatureScale = options.TemperatureScale,
});
}
}The options can be configured with both global options (ForecastDays), and options that vary depending on the current context (TemperatureScale).
using var host = FakeHost.CreateBuilder()
.ConfigureServices(services => services
.Configure<WeatherForecastOptions>(options => options.ForecastDays = 7)
.Configure<WeatherForecastOptions>(ConfigureTemperatureScaleBasedOnCountry)
.AddSingleton<WeatherForecastService>())
.Build();
static void ConfigureTemperatureScaleBasedOnCountry(IOptionsContext context, WeatherForecastOptions options)
{
CountryContextReceiver receiver = new();
context.PopulateReceiver(receiver);
if (receiver.Country == "US")
{
options.TemperatureScale = "Fahrenheit";
}
}And lastly, the service is called with some context.
var forecastService = host.Services.GetRequiredService<WeatherForecastService>();
var usForcast = await forecastService.GetForecast(new WeatherForecastContext { Country = "US" }, CancellationToken.None);
var caForcast = await forecastService.GetForecast(new WeatherForecastContext { Country = "CA" }, CancellationToken.None);We welcome feedback and contributions in our GitHub repo.