我正在使用IOptions
模式,如in the official documentation所述。如何將值更新到appsetting.json?
這項工作正常,當我讀取appsetting.json
的值時,但是如何更新值並將更改保存回appsetting.json
?
在我的情況下,我有幾個可以從用戶界面編輯的字段(由管理員用戶在應用程序中)。因此,我正在尋找通過選項訪問器來更新這些值的理想方法。
我正在使用IOptions
模式,如in the official documentation所述。如何將值更新到appsetting.json?
這項工作正常,當我讀取appsetting.json
的值時,但是如何更新值並將更改保存回appsetting.json
?
在我的情況下,我有幾個可以從用戶界面編輯的字段(由管理員用戶在應用程序中)。因此,我正在尋找通過選項訪問器來更新這些值的理想方法。
在撰寫本答案時,似乎沒有Microsoft.Extensions.Options
軟件包提供的組件提供將配置值寫回appsettings.json
的功能。
在我ASP.NET Core
項目之一,我希望能夠讓用戶改變一些應用程序設置 - 和這些設定值應存放在appsettings.json
,更precisly在可選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>
;所以只要我想讀取和寫入設置,我就可以用IWritableOptions<T>
代替IOptions<T>
。
public interface IWritableOptions<out T> : IOptions<T> where T : class, new()
{
void Update(Action<T> applyChanges);
}
另外,我想出了IOptionsWriter
,這是旨在由IWritableOptions<T>
被用於更新的結構部分的組分。這是我實現的beforementioned接口...
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
對象。如果當前值爲null
,訪問器將嘗試查找所請求的段並將其反序列化爲T
的實例,使用當前值(如果找不到)或僅創建一個新實例T
。該持有者對象將傳遞給調用者,調用者將對其應用更改。比變化的目標被轉換回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
CL屁股我可以只需要一個IWritableOptions<CustomizableOptions>
實例,它具有與IOptions<T>
相同的特性,但也允許更改和存儲配置值。
private IWritableOptions<CustomizableOptions> options;
...
this.options.Update((opt) => {
opt.SampleOption = "...";
});
非常感謝。我有關於['IConfigurationRoot']的後續問題(https://stackoverflow.com/questions/48939567/how-to-access-iconfigurationroot-in-startup-on-net-core-2),也許你知道回答:) –
簡體版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");
框架提供用於讀取配置值,通用的基礎設施,而不是對其進行修改。在修改時,您必須使用特定的配置提供程序來訪問和修改底層配置源。 – haim770
「訪問和修改底層配置源的特定配置提供程序」?請給我一些參考資料,以開始? – 439
你打算修改的配置來源是什麼? – haim770