如何将值更新到appsetting.json? [英] How to update values into appsetting.json?

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

问题描述

我正在使用IOptions模式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?

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

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();
}

我声明了扩展IOptions<T>IWritableOptions<T>接口;因此只要想读写设置,我都可以将IOptions<T>替换为IWritableOptions<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的实例,使用当前值(如果未找到),或者如果当前值为null,则仅创建一个新的T实例.然后将此持有人对象传递给调用者,调用者将对其应用更改.比更改后的对象转换回要替换该部分的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?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆