如何将值更新到appsetting.json?
我正在使用官方文档中IOptions
描述的模式.
当我从中读取值时appsetting.json
,这可以正常工作,但是如何更新值并将更改保存回来appsetting.json
?
在我的例子中,我有几个字段可以从用户界面编辑(由管理员用户在应用程序中).因此,我正在寻找通过选项访问器更新这些值的理想方法.
Matze.. 32
在编写此答案时,似乎没有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>
扩展的接口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>
更新配置部分.这是我对前面提到的接口的实现......
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 = "..."; });
ceferrari.. 30
简化版Matze的答案:
public interface IWritableOptions<out T> : IOptionsSnapshot<T> where T : class, new() { void Update(Action<T> applyChanges); } public class WritableOptions<T> : IWritableOptions<T> where T : class, new() { private readonly IHostingEnvironment _environment; private readonly IOptionsMonitor<T> _options; private readonly string _section; private readonly string _file; public WritableOptions( IHostingEnvironment environment, IOptionsMonitor<T> options, string section, string file) { _environment = environment; _options = options; _section = section; _file = file; } public T Value => _options.CurrentValue; public T Get(string name) => _options.Get(name); public void Update(Action<T> applyChanges) { var fileProvider = _environment.ContentRootFileProvider; var fileInfo = fileProvider.GetFileInfo(_file); var physicalPath = fileInfo.PhysicalPath; var jObject = JsonConvert.DeserializeObject<JObject>(File.ReadAllText(physicalPath)); var sectionObject = jObject.TryGetValue(_section, out JToken section) ? JsonConvert.DeserializeObject<T>(section.ToString()) : (Value ?? new T()); applyChanges(sectionObject); jObject[_section] = JObject.Parse(JsonConvert.SerializeObject(sectionObject)); File.WriteAllText(physicalPath, JsonConvert.SerializeObject(jObject, Formatting.Indented)); } } public static class ServiceCollectionExtensions { public static void ConfigureWritable<T>( this IServiceCollection services, IConfigurationSection section, string file = "appsettings.json") where T : class, new() { services.Configure<T>(section); services.AddTransient<IWritableOptions<T>>(provider => { var environment = provider.GetService<IHostingEnvironment>(); var options = provider.GetService<IOptionsMonitor<T>>(); return new WritableOptions<T>(environment, options, section.Key, file); }); } }
用法:
services.ConfigureWritable<MyOptions>(Configuration.GetSection("MySection"));
然后:
private readonly IWritableOptions<MyOptions> _options; public MyClass(IWritableOptions<MyOptions> options) { _options = options; }
要将更改保存到文件:
_options.Update(opt => { opt.Field1 = "value1"; opt.Field2 = "value2"; });
并且您可以将自定义json文件作为可选参数传递(默认情况下它将使用appsettings.json):
services.ConfigureWritable<MyOptions>(Configuration.GetSection("MySection"), "appsettings.custom.json");
- 我不太确定“ MyOptions”,但是阅读此内容有助于我弄清楚。https://codingblast.com/asp-net-core-configuration-reloading-binding-injecting/ (2认同)