c# - 如何将值更新到 appsettings.json 中?

标签 c# asp.net-core asp.net-core-mvc

我正在使用 IOptions 模式,如所述 in the official documentation .

当我从 appsetting.json 读取值时,这工作正常,但如何更新值并将更改保存回 appsetting.json

就我而言,我有一些可以从用户界面编辑的字段(由应用程序中的管理员用户)。因此,我正在寻找通过选项访问器更新这些值的理想方法。

最佳答案

在撰写此答案时,Microsoft.Extensions.Options 似乎没有提供任何组件。具有将配置值写回 appsettings.json 功能的软件包.

在我的一个 ASP.NET Core 中我想让用户能够更改一些应用程序设置的项目 - 这些设置值应存储在 appsettings.json 中,更准确地说是可选的appsettings.custom.json文件,如果存在,则会添加到配置中。

像这样...

public Startup(IHostingEnvironment env)
{
    IConfigurationBuilder builder = new ConfigurationBuilder()
        .SetBasePath(env.ContentRootPath)
        .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
        .AddJsonFile("appsettings.custom.json", optional: true, reloadOnChange: true)
        .AddEnvironmentVariables();

    this.Configuration = builder.Build();
}

我声明了IWritableOptions<T>扩展的接口(interface)IOptions<T> ;所以我可以替换 IOptions<T>通过IWritableOptions<T>每当我想读取和写入设置时。

public interface IWritableOptions<out T> : IOptions<T> where T : class, new()
{
    void Update(Action<T> applyChanges);
}

此外,我想出了 IOptionsWriter ,这是一个供 IWritableOptions<T> 使用的组件更新配置部分。这是我对前面提到的接口(interface)的实现...

class OptionsWriter : IOptionsWriter
{
    private readonly IHostingEnvironment environment;
    private readonly IConfigurationRoot configuration;
    private readonly string file;

    public OptionsWriter(
        IHostingEnvironment environment, 
        IConfigurationRoot configuration, 
        string file)
    {
        this.environment = environment;
        this.configuration = configuration;
        this.file = file;
    }

    public void UpdateOptions(Action<JObject> callback, bool reload = true)
    {
        IFileProvider fileProvider = this.environment.ContentRootFileProvider;
        IFileInfo fi = fileProvider.GetFileInfo(this.file);
        JObject config = fileProvider.ReadJsonFileAsObject(fi);
        callback(config);
        using (var stream = File.OpenWrite(fi.PhysicalPath))
        {
            stream.SetLength(0);
            config.WriteTo(stream);
        }

        this.configuration.Reload();
    }
}

由于作者不了解文件结构,我决定将部分处理为 JObject对象。访问器尝试查找请求的部分并将其反序列化为 T 的实例,使用当前值(如果未找到),或者仅创建 T 的新实例,如果当前值为 null 。然后,该持有者对象被传递给调用者,调用者将对其应用更改。更改后的对象将转换回 JToken将替换该部分的实例...

class WritableOptions<T> : IWritableOptions<T> where T : class, new()
{
    private readonly string sectionName;
    private readonly IOptionsWriter writer;
    private readonly IOptionsMonitor<T> options;

    public WritableOptions(
        string sectionName, 
        IOptionsWriter writer, 
        IOptionsMonitor<T> options)
    {
        this.sectionName = sectionName;
        this.writer = writer;
        this.options = options;
    }

    public T Value => this.options.CurrentValue;

    public void Update(Action<T> applyChanges)
    {
        this.writer.UpdateOptions(opt =>
        {
            JToken section;
            T sectionObject = opt.TryGetValue(this.sectionName, out section) ?
                JsonConvert.DeserializeObject<T>(section.ToString()) :
                this.options.CurrentValue ?? new T();

            applyChanges(sectionObject);

            string json = JsonConvert.SerializeObject(sectionObject);
            opt[this.sectionName] = JObject.Parse(json);
        });
    }
}

最后,我为IServicesCollection实现了一个扩展方法。允许我轻松配置可写选项访问器...

static class ServicesCollectionExtensions
{
    public static void ConfigureWritable<T>(
        this IServiceCollection services, 
        IConfigurationRoot configuration, 
        string sectionName, 
        string file) where T : class, new()
    {
        services.Configure<T>(configuration.GetSection(sectionName));

        services.AddTransient<IWritableOptions<T>>(provider =>
        {
            var environment = provider.GetService<IHostingEnvironment>();
            var options = provider.GetService<IOptionsMonitor<T>>();
            IOptionsWriter writer = new OptionsWriter(environment, configuration, file);
            return new WritableOptions<T>(sectionName, writer, options);
        });
    }
}

可用于ConfigureServices就像...

services.ConfigureWritable<CustomizableOptions>(this.Configuration, 
    "MySection", "appsettings.custom.json");

在我的Controller类我可以要求 IWritableOptions<CustomizableOptions>例如,具有与 IOptions<T> 相同的特征,但也允许更改和存储配置值。

private IWritableOptions<CustomizableOptions> options;

...

this.options.Update((opt) => {
    opt.SampleOption = "...";
});

关于c# - 如何将值更新到 appsettings.json 中?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40970944/

相关文章:

c# - 无效泛型类型参数的最佳异常(exception)

entity-framework - 如何在 EF7/.NET Core 中为多个数据库实现 DbContext 继承

c# - 字符串路由约束

c# - 在 C# 中将网站动态翻译成阿拉伯语

c# - 区分 machine.config 和 web.config 中的 ConnectionString

c# - 由于到期日期更改 gridview 列颜色

c# - ASP.NET 核心 WebSocket

angular - 在升级到 Angular 9 期间,Typescript 编译中缺少 main.ts 和 polyfills.ts

asp.net-core - 如何根据 ASP.NET Core 中的解决方案配置运行脚本

asp.net-core - 开发过程中 Web.config 在哪里?