I move an application to .net core and trying to use IOptions pattern.
My application is multi tenant with single database. System has global default options and I keep them in database (same as my old application) and also each tenant has own options. If tenant has no option with a key in global, so I need to use global option.
In configuration, I handle to getting global options from database. It is easy with example in documentation.
However, each tenant options not going well. Although I actually know what I want, I don't know how to do it in .Net Core.
I test in a console application.
class Program {
static void Main(string[] args) {
var services = ConfigureServices();
var serviceProvider = services.BuildServiceProvider();
serviceProvider.GetService<App>().Run();
}
public class App {
private readonly IOptionsSnapshot<DemoOptions> _options;
public App(IOptionsSnapshot<DemoOptions> options) {
_options = options;
}
public void Run() {
Console.WriteLine("Hello from App.cs");
Console.WriteLine($"DemoOptions:Global:Enabled={_options.Value.Enabled}");
Console.WriteLine($"DemoOptions:Global:AutoRetryDelay={_options.Value.AutoRetryDelay}");
Console.WriteLine($"DemoOptions:Global:IdentityOptions:MaxUserNameLength={_options.Value.IdentityOptions.MaxUserNameLength}");
}
}
private static IServiceCollection ConfigureServices() {
IServiceCollection services = new ServiceCollection();
//Load global configuration from database and use them.
var config = LoadConfiguration();
services.AddSingleton(config);
services.AddDbContext<EntityConfigurationContext>(options => options.UseInMemoryDatabase("InMemoryDb"));
services.AddScoped<ITenantService, TenantService>();
//I take this part from example link in below. But I am not successed.
services.AddSingleton<IOptionsMonitorCache<DemoOptions>,TenantOptionsCache<DemoOptions>>();
services.AddTransient<IOptionsFactory<DemoOptions>,TenantOptionsFactory<DemoOptions>>();
services.AddScoped<IOptionsSnapshot<DemoOptions>,TenantOptions<DemoOptions>>();
services.AddSingleton<IOptions<DemoOptions>,TenantOptions<DemoOptions>>();
// required to run the application
services.AddTransient<App>();
return services;
}
public static IConfiguration LoadConfiguration() {
var builder = new ConfigurationBuilder();
builder.Sources.Clear();
builder.AddEntityConfiguration(options => options.UseInMemoryDatabase("InMemoryDb"));
IConfigurationRoot configurationRoot = builder.Build();
DemoOptions options = new();
configurationRoot.GetSection($"{nameof(DemoOptions)}:{DemoOptions.Global}").Bind(options);
Console.WriteLine($"DemoOptions:Global:Enabled={options.Enabled}");
Console.WriteLine($"DemoOptions:Global:AutoRetryDelay={options.AutoRetryDelay}");
Console.WriteLine($"DemoOptions:Global:IdentityOptions:MaxUserNameLength={options.IdentityOptions.MaxUserNameLength}");
return builder.Build();
}
}
public record DemoOptions {
public const string Global = nameof(Global);
public const string Tenant = nameof(Tenant);
public bool Enabled { get; set; }
public TimeSpan AutoRetryDelay { get; set; }
public IdentityOptions IdentityOptions { get; set; }
}
public record IdentityOptions {
public int MaxUserNameLength { get; set; }
}
public record DemoSettings(string Key, string Value) {
public int Id { get; set; }
}
public record TenantSettings(string Key, string Value, int TenantId) {
public int Id { get; set; }
}
I add some important class here. However if you want to look at all project, I add github link. I use this example