如何将值更新到 appsetting.json?

How to update values into appsetting.json?(如何将值更新到 appsetting.json?)
本文介绍了如何将值更新到 appsetting.json?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我使用 IOptions 模式-configuration-objects" rel="noreferrer">在官方文档中.

I am using the IOptions pattern as described in the official documentation.

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

This works fine when I am reading values from appsetting.json, but how do I update values and save changes back to appsetting.json?

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

In my case, I have a few fields that can be edited from the user interface (by admin user in application). Hence I am looking for the ideal approach to update these values via the option accessor.

推荐答案

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

At the time of writing this answer it seemed that there is no component provided by the Microsoft.Extensions.Options package that has functionality to write configuration values back to appsettings.json.

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

In one of my ASP.NET Core projects I wanted to enable the user to change some application settings - and those setting values should be stored in appsettings.json, more precisly in an optional appsettings.custom.json file, that gets added to the configuration if present.

像这样……

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> 接口,它扩展了 IOptions<T>;所以只要我想读写设置,我就可以用 IWritableOptions<T> 替换 IOptions<T>.

I declared the IWritableOptions<T> interface that extends IOptions<T>; so I can just replace IOptions<T> by IWritableOptions<T> whenever I want to read and write settings.

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

另外,我想出了 IOptionsWriter,它是一个旨在由 IWritableOptions<T> 用于更新配置部分的组件.这是我对前面提到的接口的实现...

Also, I came up with IOptionsWriter, which is a component that is intended to be used by IWritableOptions<T> to update a configuration section. This is my implementation for the beforementioned interfaces...

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 实例,该实例将替换该部分...

Since the writer is not aware about the file structure, I decided to handle sections as JObject objects. The accessor tries to find the requested section and deserializes it to an instance of T, uses the current value (if not found), or just creates a new instance of T, if the current value is null. This holder object is than passed to the caller, who will apply the changes to it. Than the changed object gets converted back to a JToken instance that is going to replace the section...

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 实现了一个扩展方法,允许我轻松配置可写选项访问器...

Finally, I implemented an extension method for IServicesCollection allowing me to easily configure a writable options accessor...

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中使用类似...

Which can be used in ConfigureServices like...

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

在我的 Controller 类中,我只需要一个 IWritableOptions<CustomizableOptions> 实例,它与 IOptions<T> 具有相同的特性,但也允许更改和存储配置值.

In my Controller class I can just demand an IWritableOptions<CustomizableOptions> instance, that has the same characteristics as IOptions<T>, but also allows to change and store configuration values.

private IWritableOptions<CustomizableOptions> options;

...

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

这篇关于如何将值更新到 appsetting.json?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持编程学习网!

本站部分内容来源互联网,如果有图片或者内容侵犯您的权益请联系我们删除!

相关文档推荐

DispatcherQueue null when trying to update Ui property in ViewModel(尝试更新ViewModel中的Ui属性时DispatcherQueue为空)
Drawing over all windows on multiple monitors(在多个监视器上绘制所有窗口)
Programmatically show the desktop(以编程方式显示桌面)
c# Generic Setlt;Tgt; implementation to access objects by type(按类型访问对象的C#泛型集实现)
InvalidOperationException When using Context Injection in ASP.Net Core(在ASP.NET核心中使用上下文注入时发生InvalidOperationException)
LINQ many-to-many relationship, how to write a correct WHERE clause?(LINQ多对多关系,如何写一个正确的WHERE子句?)