简体   繁体   中英

Using IOptions and load options from database with multi tenant application

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

I see. You ought to do these things

  1. install a nuget package

Microsoft.Extensions.Options.ConfigurationExtensions

  1. In your Programs.cs in ConfigureServices replace this code
    var config = LoadConfiguration();
    services.Configure<DemoOptions>(config.GetSection($"{nameof(DemoOptions)}:{DemoOptions.Global}"));
    services.AddSingleton(config);

You can try ready-to-useJsonRepositoryConfiguration Nuget package. It also has a auto-refresh feature and works pretty much like configuration form JSON config files, only it expects the JSON config from your repository which can in turn bring the data from any internal/external storage including making a database call, API call etc.

You can mix different configuration provides (like this one and JSON config files) and use them together should you like to. Confession: I am the author.

The next step would be using Named Options .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM