将 IConfigurationRoot 部分的更改保存到 .net Core 2.2 中的 *.json 文件

Posted

技术标签:

【中文标题】将 IConfigurationRoot 部分的更改保存到 .net Core 2.2 中的 *.json 文件【英文标题】:Save Changes of IConfigurationRoot sections to its *.json file in .net Core 2.2 【发布时间】:2020-01-18 13:42:16 【问题描述】:

我一直在寻找解决方案,但没有找到,我打赌有人遇到过这个问题,那么问题是什么?

为了测试,我创建了简单的控制台应用程序(解决方案将用于 asp.net core web api)。

我已经设置了“始终复制”的 TestSetting.json 配置文件。


  "setting1" : "value1" 

简单的代码

IConfigurationBuilder configurationBuilder = new ConfigurationBuilder();

IConfigurationRoot configuration = configurationBuilder.AddJsonFile("TestSettings.json",false, reloadOnChange: true).Build();

Console.WriteLine(configuration.GetSection("setting1").Value); // Output: value1
//Change configuration manually in file while console is waiting
Console.ReadKey();

//Changed manually in file Value appears
Console.WriteLine(configuration.GetSection("setting1").Value); // Output: Whatever you have setuped
Console.ReadKey();

configuration.GetSection("setting1").Value = "changed from code";
//Changed in code value appear
Console.WriteLine(configuration.GetSection("setting1").Value); // Output: changed from code

我有 2 个要求,我希望能够在应用程序运行时手动更改 json 配置文件中的值,并且应用程序将在下一次获取设置部分时看到更新的值并且它正在工作。

第二个要求是,我想保留一些信息,准确地说是任务的最后执行时间,应该在每个设置的周期前执行一次。每天一次,因此一些循环检查最后执行时间值并确定是否必须执行操作。有人会问我有什么可以工作,但我还需要涵盖执行操作和重新启动应用程序的场景(服务器错误、用户重新启动等),我需要以允许我的方式保存这些信息应用启动后阅读。

阅读代码示例,我们可以看到在更改代码中的设置 1 后,我们看到该部分在尝试将其输出到控制台时已更改。

configuration.GetSection("setting1").Value = "changed from code";
//Changed in code value appear
Console.WriteLine(configuration.GetSection("setting1").Value); // Output: changed from code

问题来了:)。此设置部分更改是否可能也会影响 json 文件中的实际值?我不想由某些流作家或其他人手动更改此文件。

实际结果是: 在代码中更改值后,新值可以在运行时获取,但是当您去调试二进制文件时,您会发现 TestSettings.json 文件中的 value1 没有更改。

【问题讨论】:

【参考方案1】:

谢谢“Matt Luccas Phaure Jensen” 对于这个信息,我在那里没有找到任何解决方案。如果有人想在这里使用 IOptions 是一个答案如何做到这一点How to update values into appsetting.json?

我想按照我开始的方式来做,所以我查看了 Microsoft.Extensions.Configuration.Json 的实现,并且我准备了简单的解决方案来允许编写和使用基本实现。它可能有很多限制,但它可以在简单的场景中工作。

必须扩展上述 dll 的两个实现。所以让我们去做吧。

WritableJsonConfigurationProvider 的实现以及所需部分的示例保存代码。

Change values in JSON file (writing files)

public class WritableJsonConfigurationProvider : JsonConfigurationProvider

    public WritableJsonConfigurationProvider(JsonConfigurationSource source) : base(source)
    
    

    public override void Set(string key, string value)
    
        base.Set(key,value);

        //Get Whole json file and change only passed key with passed value. It requires modification if you need to support change multi level json structure
        var fileFullPath = base.Source.FileProvider.GetFileInfo(base.Source.Path).PhysicalPath;
        string json = File.ReadAllText(fileFullPath);
        dynamic jsonObj = JsonConvert.DeserializeObject(json);
        jsonObj[key] = value;
        string output = JsonConvert.SerializeObject(jsonObj, Formatting.Indented);
        File.WriteAllText(fileFullPath, output);
    

以及 WritableJsonConfigurationSource 的实现,它是 JsonConfigurationSource 的扩展

public class WritableJsonConfigurationSource : JsonConfigurationSource

    public override IConfigurationProvider Build(IConfigurationBuilder builder)
    
        this.EnsureDefaults(builder);
        return (IConfigurationProvider)new WritableJsonConfigurationProvider(this);
    

就是这样,让我们​​使用它

IConfigurationBuilder configurationBuilder = new ConfigurationBuilder();
IConfigurationRoot configuration = configurationBuilder.Add<WritableJsonConfigurationSource>(
    (Action<WritableJsonConfigurationSource>)(s =>
                                                     
                                                         s.FileProvider = null;
                                                         s.Path = "TestSettings.json";
                                                         s.Optional = false;
                                                         s.ReloadOnChange = true;
                                                         s.ResolveFileProvider();
                                                     )).Build();

Console.WriteLine(configuration.GetSection("setting1").Value); // Output: value1
Console.ReadKey();

configuration.GetSection("setting1").Value = "changed from codeeee";
Console.WriteLine(configuration.GetSection("setting1").Value); // Output: changed from codeeee

Console.ReadKey();

内存中的值以及文件中的值都在更改。 宾果游戏:)

代码可能有问题,可以重构等,这只是示例快速解决方案。

【讨论】:

有效!但不适用于部分。 var fromFolder = _configurationRoot.GetSection("AppSettings:FromFileFolder").Value - 这会得到正确的值(尽管为什么是 GetSection?它会得到一个值)_configurationRoot.GetSection("AppSettings:FromFileFolder").Value = "xxx" - 这会在名为“AppSettings:FromFileFolder”的配置文件的根目录中创建一个新值。不幸的是,这不是我想要的。【参考方案2】:

无法通过 Microsoft.Extensions.Configuration 包将对配置所做的更改保存到磁盘。 在 github 上有一个关于它的问题,他们选择不这样做。 可以这样做,只是不通过 IConfiguration 接口。 https://github.com/aspnet/Configuration/issues/385

【讨论】:

【参考方案3】:

首先感谢您对这个 Pawel 的回答,您的覆盖方法对我帮助很大。

这与 Pawel 的回答和 Kirk Hawley 的评论有关

@柯克·霍利

它适用于您只需要调整覆盖以匹配您的 json 模式的部分。

您可以使用 ConfigurationSection.Path 从部分中获取路径。

public static string KeyPairPath  get; set; 
MyPublicClass.KeyPairPath = mysection.Path;

由于覆盖函数的 key 参数只包含最后一个键而不是完整的键(与父节),如果您使用子节,您可以将节的完整路径作为类的静态成员移交.例如:

public override void Set(string key, string value)

            string currentPath = MyPublicClass.KeyPairPath;

            string[] getParent = currentPath.Split(':');
            string parent = getParent[0];

            key = parent + ":" + key;

            string[] substantialKey = key.Split(":");
            
            base.Set(key, value);

            var fileFullPath = base.Source.FileProvider.GetFileInfo(base.Source.Path).PhysicalPath;
            string json = File.ReadAllText(fileFullPath);
            dynamic jsonObj = JsonConvert.DeserializeObject(json);
            jsonObj[parent][substantialKey[1]] = value;

            string output = JsonConvert.SerializeObject(jsonObj, Formatting.Indented);
            File.WriteAllText(fileFullPath, output);

这对我有用,可以更新我的 appsettings.json。

【讨论】:

以上是关于将 IConfigurationRoot 部分的更改保存到 .net Core 2.2 中的 *.json 文件的主要内容,如果未能解决你的问题,请参考以下文章

如何在.net core 2 的启动中访问 IConfigurationRoot?

尝试激活'TheWorld.Startup'时无法解析类型'Microsoft.Extensions.Configuration.IConfigurationRoot'

在 Rails 中显示/隐藏部分/模板的更干燥的方式

将数据表写入excel的更有效方法?

将 Rails 参数哈希传递给命名路由的更有效方法

将QLineEdit的宽度调整为内容(并且比预期的更短)