ASP.NET 核心 appsettings.json 代码更新
ASP.NET Core appsettings.json update in code
我目前正在使用 asp.net 核心 v1.1 进行项目,在我的 appsettings.json 中我有:
"AppSettings": {
"AzureConnectionKey": "***",
"AzureContainerName": "**",
"NumberOfTicks": 621355968000000000,
"NumberOfMiliseconds": 10000,
"SelectedPvInstalationIds": [ 13, 137, 126, 121, 68, 29 ],
"MaxPvPower": 160,
"MaxWindPower": 5745.35
},
我还有 class 用来存储它们:
public class AppSettings
{
public string AzureConnectionKey { get; set; }
public string AzureContainerName { get; set; }
public long NumberOfTicks { get; set; }
public long NumberOfMiliseconds { get; set; }
public int[] SelectedPvInstalationIds { get; set; }
public decimal MaxPvPower { get; set; }
public decimal MaxWindPower { get; set; }
}
然后在 Startup.cs:
启用 DI
services.Configure<AppSettings>(Configuration.GetSection("AppSettings"));
有什么方法可以从 Controller 更改和保存 MaxPvPower
和 MaxWindPower
吗?
我试过使用
private readonly AppSettings _settings;
public HomeController(IOptions<AppSettings> settings)
{
_settings = settings.Value;
}
[Authorize(Policy = "AdminPolicy")]
public IActionResult UpdateSettings(decimal pv, decimal wind)
{
_settings.MaxPvPower = pv;
_settings.MaxWindPower = wind;
return Redirect("Settings");
}
但它什么也没做。
这是一篇来自 Microsoft 的有关 .Net Core 应用程序配置设置的相关文章:
该页面还有 sample code 也可能有帮助。
更新
我认为 In-memory provider and binding to a POCO class 可能会有一些用处,但没有像 OP 预期的那样工作。
下一个选项可以在添加配置文件时将 AddJsonFile 的 reloadOnChange
参数设置为 true 和
手动解析 JSON 配置文件并按预期进行更改。
public class Startup
{
...
public Startup(IHostingEnvironment env)
{
var builder = new ConfigurationBuilder()
.SetBasePath(env.ContentRootPath)
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true)
.AddEnvironmentVariables();
Configuration = builder.Build();
}
...
}
... reloadOnChange
is only supported in ASP.NET Core 1.1 and higher.
基本上,您可以像这样设置 IConfiguration
中的值:
IConfiguration configuration = ...
// ...
configuration["key"] = "value";
问题在于,例如JsonConfigurationProvider
没有实现将配置保存到文件中。正如您在 source it does not override the Set method of ConfigurationProvider
. (see source)
中看到的
您可以创建自己的提供程序并在那里实现保存。 Here (Basic sample of Entity Framework custom provider) 是如何操作的示例。
假设 appsettings.json 有一个 eureka 端口,并且想在 args (-p 5090) 中动态更改它。通过这样做,可以在创建许多服务时轻松地为 docker 更改端口。
"eureka": {
"client": {
"serviceUrl": "http://10.0.0.101:8761/eureka/",
"shouldRegisterWithEureka": true,
"shouldFetchRegistry": false
},
"instance": {
"port": 5000
}
}
public class Startup
{
public static string port = "5000";
public Startup(IConfiguration configuration)
{
configuration["eureka:instance:port"] = port;
Configuration = configuration;
}
public static void Main(string[] args)
{
int port = 5000;
if (args.Length>1)
{
if (int.TryParse(args[1], out port))
{
Startup.port = port.ToString();
}
}
}
在运行时更新 ASP.NET 核心中的 appsettings.json
文件。
获取此示例 appsettings.json
文件:
{
Config: {
IsConfig: false
}
}
这是将 IsConfig
属性 更新为 true 的代码:
Main()
{
AddOrUpdateAppSetting("Config:IsConfig", true);
}
public static void AddOrUpdateAppSetting<T>(string key, T value)
{
try
{
var filePath = Path.Combine(AppContext.BaseDirectory, "appSettings.json");
string json = File.ReadAllText(filePath);
dynamic jsonObj = Newtonsoft.Json.JsonConvert.DeserializeObject(json);
var sectionPath = key.Split(":")[0];
if (!string.IsNullOrEmpty(sectionPath))
{
var keyPath = key.Split(":")[1];
jsonObj[sectionPath][keyPath] = value;
}
else
{
jsonObj[sectionPath] = value; // if no sectionpath just set the value
}
string output = Newtonsoft.Json.JsonConvert.SerializeObject(jsonObj, Newtonsoft.Json.Formatting.Indented);
File.WriteAllText(filePath, output);
}
catch (ConfigurationErrorsException)
{
Console.WriteLine("Error writing app settings");
}
}
public static void SetAppSettingValue(string key, string value, string appSettingsJsonFilePath = null)
{
if (appSettingsJsonFilePath == null)
{
appSettingsJsonFilePath = System.IO.Path.Combine(System.AppContext.BaseDirectory, "appsettings.json");
}
var json = System.IO.File.ReadAllText(appSettingsJsonFilePath);
dynamic jsonObj = Newtonsoft.Json.JsonConvert.DeserializeObject<Newtonsoft.Json.Linq.JObject>(json);
jsonObj[key] = value;
string output = Newtonsoft.Json.JsonConvert.SerializeObject(jsonObj, Newtonsoft.Json.Formatting.Indented);
System.IO.File.WriteAllText(appSettingsJsonFilePath, output);
}
我正在使用我自己的配置部分和我自己的强类型对象。我总是用这个强类型对象注入 IOptions。而且我能够在运行时更改配置。要非常小心对象的范围。新的配置值由请求范围的对象选取。我正在使用构造函数注入。
关于这方面的文档还很不清楚。我不确定这是否是故意的。阅读此 in-depth discussion
我获取了 Qamar Zamans 的代码(谢谢)并对其进行了修改以允许编辑 more:than:one:layer:deep 的参数。
希望它能帮到一些人,惊讶的是这不是某处的图书馆功能。
public static class SettingsHelpers
{
public static void AddOrUpdateAppSetting<T>(string sectionPathKey, T value)
{
try
{
var filePath = Path.Combine(AppContext.BaseDirectory, "appsettings.json");
string json = File.ReadAllText(filePath);
dynamic jsonObj = Newtonsoft.Json.JsonConvert.DeserializeObject(json);
SetValueRecursively(sectionPathKey, jsonObj, value);
string output = Newtonsoft.Json.JsonConvert.SerializeObject(jsonObj, Newtonsoft.Json.Formatting.Indented);
File.WriteAllText(filePath, output);
}
catch (Exception ex)
{
Console.WriteLine("Error writing app settings | {0}", ex.Message);
}
}
private static void SetValueRecursively<T>(string sectionPathKey, dynamic jsonObj, T value)
{
// split the string at the first ':' character
var remainingSections = sectionPathKey.Split(":", 2);
var currentSection = remainingSections[0];
if (remainingSections.Length > 1)
{
// continue with the procress, moving down the tree
var nextSection = remainingSections[1];
SetValueRecursively(nextSection, jsonObj[currentSection], value);
}
else
{
// we've got to the end of the tree, set the value
jsonObj[currentSection] = value;
}
}
有一个更简单的答案可以在运行时修改 appsettings.json。
Json File structure
var filePath = Path.Combine(System.AppContext.BaseDirectory, "appSettings.json");
string jsonString = System.IO.File.ReadAllText(filePath);
//use https://json2csharp.com/ to create the c# classes from your json
Root root = JsonSerializer.Deserialize<Root>(jsonString);
var dbtoadd = new Databas()
{
Id = "myid",
Name = "mynewdb",
ConnectionString = ""
};
//add or change anything to this object like you do on any list
root.DatabaseSettings.Databases.Add(dbtoadd);
//serialize the new updated object to a string
string towrite = JsonSerializer.Serialize(root);
//overwrite the file and it wil contain the new data
System.IO.File.WriteAllText(filePath, towrite);
根据 Qamar Zaman 和 Alex Horlock 的代码,我稍微修改了一下。
public static class SettingsHelpers
{
public static void AddOrUpdateAppSetting<T>(T value, IWebHostEnvironment webHostEnvironment)
{
try
{
var settingFiles = new List<string> { "appsettings.json", $"appsettings.{webHostEnvironment.EnvironmentName}.json" };
foreach (var item in settingFiles)
{
var filePath = Path.Combine(AppContext.BaseDirectory, item);
string json = File.ReadAllText(filePath);
dynamic jsonObj = Newtonsoft.Json.JsonConvert.DeserializeObject(json);
SetValueRecursively(jsonObj, value);
string output = Newtonsoft.Json.JsonConvert.SerializeObject(jsonObj, Newtonsoft.Json.Formatting.Indented);
File.WriteAllText(filePath, output);
}
}
catch (Exception ex)
{
throw new Exception($"Error writing app settings | {ex.Message}", ex);
}
}
private static void SetValueRecursively<T>(dynamic jsonObj, T value)
{
var properties = value.GetType().GetProperties();
foreach (var property in properties)
{
var currentValue = property.GetValue(value);
if (property.PropertyType.IsPrimitive || property.PropertyType == typeof(string) || property.PropertyType == typeof(decimal))
{
if (currentValue == null) continue;
try
{
jsonObj[property.Name].Value = currentValue;
}
catch (RuntimeBinderException)
{
jsonObj[property.Name] = new JValue(currentValue);
}
continue;
}
try
{
if (jsonObj[property.Name] == null)
{
jsonObj[property.Name] = new JObject();
}
}
catch (RuntimeBinderException)
{
jsonObj[property.Name] = new JObject(new JProperty(property.Name));
}
SetValueRecursively(jsonObj[property.Name], currentValue);
}
}
}
我解决这个问题的方法是添加一个“覆盖”属性 存储在内存缓存中。因此,例如,我的应用程序在“appSettings.json”文件中有一个“CacheEnabled”设置,用于确定是否缓存数据查询结果。在应用程序/数据库测试期间,有时需要将此 属性 设置为“false”。
通过管理员菜单,管理员可以覆盖“CacheEnabled”设置。确定缓存是否启用的逻辑首先检查覆盖。如果找不到覆盖值,则使用“appSettings.json”值。
考虑到实施它需要额外的基础设施,对于很多人来说这可能不是一个好的解决方案。但是,我的应用程序已经有一个缓存服务和一个管理员菜单,所以它很容易实现。
我看到大多数答案都使用 Newtonsoft.Json
包来更新设置。如果您需要更新一层深的设置,您可以不使用 Newtonsoft.Json
并使用 System.Text.Json
(内置于 .Net Core 3.0 及更高版本)功能。这是一个简单的实现:
public void UpdateAppSetting(string key, string value)
{
var configJson = File.ReadAllText("appsettings.json");
var config = JsonSerializer.Deserialize<Dictionary<string, object>>(configJson);
config[key] = value;
var updatedConfigJson = JsonSerializer.Serialize(config, new JsonSerializerOptions { WriteIndented = true });
File.WriteAllText("appsettings.json", updatedConfigJson);
}
在我的项目中,我以这种方式使用 Active Directory 设置:
//...
public class Startup
{
public void ConfigureServices(IServicesCollection services)
{
//...
services.Configure<Ldap>(opts=> {
opts.Url = "example.com";
opts.UseSsl = true;
opts.Port = 111;
opts.BindDn = "CN=nn,OU=nn,OU=nn,DC=nn,DC=nn";
opts.BindCredentials = "nn";
opts.SearchBase = "DC=nn,DC=nn";
opts.SearchFilter = "(&(objectClass=User){0})";
opts.AdminCn = "CN=nn,OU=nn,OU=nn,DC=nn,DC=nn";
opts.SearchGroupBase = "OU=nn,DC=nn,DC=nn";
});
//...
}
}
因此,不使用 appsettings.json。
之后我可以从控制器更新此设置:
//...
[HttpPost("setActiveDirectorySettings")]
public ActionResult<IOptions<Ldap>> SetActiveDirectorySettings(ActiveDirectorySettings clientActiveDirectorySettings)
{
LdapOptions.Value.Url = clientActiveDirectorySettings.Url;
LdapOptions.Value.UseSsl = clientActiveDirectorySettings.UseSsl;
LdapOptions.Value.Port = clientActiveDirectorySettings.Port;
LdapOptions.Value.BindDn = clientActiveDirectorySettings.BindDn;
LdapOptions.Value.BindCredentials = clientActiveDirectorySettings.BindCredentials;
LdapOptions.Value.SearchBase = clientActiveDirectorySettings.SearchBase;
LdapOptions.Value.SearchFilter = clientActiveDirectorySettings.SearchFilter;
LdapOptions.Value.AdminCn = clientActiveDirectorySettings.AdminCn;
LdapOptions.Value.SearchGroupBase = clientActiveDirectorySettings.SearchGroupBase;
return Ok(LdapOptions.Value);
}
//...
看起来对我有用
基于@Alper Ebicoglu 的回答
获取:
// ===== || GET || GET appsettings.js property =====================================================================
[HttpGet]
[Route("GetNotificationDays")]
public async Task<IActionResult> GetNotificationDays()
{
var path = System.IO.Path.Combine(Directory.GetCurrentDirectory(), "appsettings.json");
var json = await System.IO.File.ReadAllTextAsync(path);
dynamic jsonObj = Newtonsoft.Json.JsonConvert.DeserializeObject<Newtonsoft.Json.Linq.JObject>(json);
return StatusCode(200, new { daysBefore = (int)jsonObj.InvoicementNotificationSettings.DaysBefore});
}
Exp:
(int)jsonObj.InvoicementNotificationSettings.DaysBefore =
(int) = cast to int - depending on the property
jsonObj = appsettings.js,
InvoicementNotificationSettings = object in appsettings.js,
DaysBefore = property in InvoicementNotificationSettings
更新:appsettings.js
// ===== || PUT || UPDATE appsettings.js property =====================================================================
[HttpPut]
[Route("SetNotificationDays")]
public async Task<IActionResult> SetNotificationDays(int notificationDays)
{
if (notificationDays != 0)
{
var path = System.IO.Path.Combine(Directory.GetCurrentDirectory(), "appsettings.json");
var json = await System.IO.File.ReadAllTextAsync(path);
dynamic jsonObj = Newtonsoft.Json.JsonConvert.DeserializeObject<Newtonsoft.Json.Linq.JObject>(json);
jsonObj.InvoicementNotificationSettings.DaysBefore = notificationDays;
string output = Newtonsoft.Json.JsonConvert.SerializeObject(jsonObj, Newtonsoft.Json.Formatting.Indented);
await System.IO.File.WriteAllTextAsync(path, output);
return await GetNotificationDays();
}
return StatusCode(409);
}
如果从内存中读取应用程序设置:
例如:int daysBefore = configuration.GetValue<int>("InvoicementNotificationSettings:DaysBefore");
比 Startup.js - 更新后自动重新加载 appsettings.js
public class Startup
{
public static IConfiguration Configuration { get; set; }
// Constructor -----------------------------------------------------------------------------------------------------------------------------
public Startup(IConfiguration configuration, Microsoft.Extensions.Hosting.IHostEnvironment env)
{
Configuration = configuration;
// To autoreload appsettings.js after update -------------------------
var builder = new ConfigurationBuilder()
.SetBasePath(env.ContentRootPath)
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true)
.AddEnvironmentVariables();
Configuration = builder.Build();
}
appsettings.js
{
"ConnectionStrings": {
"DefaultConnection": "Data Source=(localdb)\MSSQLLocalDB;Initial Catalog=ItlCrmsDb;Integrated Security=True;Connect Timeout=30;Encrypt=False;TrustServerCertificate=False;ApplicationIntent=ReadWrite;MultiSubnetFailover=False"
},
"InvoicementNotificationSettings": {
"DaysBefore": 4
},
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
},
"AllowedHosts": "*"
}
我目前正在使用 asp.net 核心 v1.1 进行项目,在我的 appsettings.json 中我有:
"AppSettings": {
"AzureConnectionKey": "***",
"AzureContainerName": "**",
"NumberOfTicks": 621355968000000000,
"NumberOfMiliseconds": 10000,
"SelectedPvInstalationIds": [ 13, 137, 126, 121, 68, 29 ],
"MaxPvPower": 160,
"MaxWindPower": 5745.35
},
我还有 class 用来存储它们:
public class AppSettings
{
public string AzureConnectionKey { get; set; }
public string AzureContainerName { get; set; }
public long NumberOfTicks { get; set; }
public long NumberOfMiliseconds { get; set; }
public int[] SelectedPvInstalationIds { get; set; }
public decimal MaxPvPower { get; set; }
public decimal MaxWindPower { get; set; }
}
然后在 Startup.cs:
启用 DIservices.Configure<AppSettings>(Configuration.GetSection("AppSettings"));
有什么方法可以从 Controller 更改和保存 MaxPvPower
和 MaxWindPower
吗?
我试过使用
private readonly AppSettings _settings;
public HomeController(IOptions<AppSettings> settings)
{
_settings = settings.Value;
}
[Authorize(Policy = "AdminPolicy")]
public IActionResult UpdateSettings(decimal pv, decimal wind)
{
_settings.MaxPvPower = pv;
_settings.MaxWindPower = wind;
return Redirect("Settings");
}
但它什么也没做。
这是一篇来自 Microsoft 的有关 .Net Core 应用程序配置设置的相关文章:
该页面还有 sample code 也可能有帮助。
更新
我认为 In-memory provider and binding to a POCO class 可能会有一些用处,但没有像 OP 预期的那样工作。
下一个选项可以在添加配置文件时将 AddJsonFile 的 reloadOnChange
参数设置为 true 和
手动解析 JSON 配置文件并按预期进行更改。
public class Startup
{
...
public Startup(IHostingEnvironment env)
{
var builder = new ConfigurationBuilder()
.SetBasePath(env.ContentRootPath)
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true)
.AddEnvironmentVariables();
Configuration = builder.Build();
}
...
}
...
reloadOnChange
is only supported in ASP.NET Core 1.1 and higher.
基本上,您可以像这样设置 IConfiguration
中的值:
IConfiguration configuration = ...
// ...
configuration["key"] = "value";
问题在于,例如JsonConfigurationProvider
没有实现将配置保存到文件中。正如您在 source it does not override the Set method of ConfigurationProvider
. (see source)
您可以创建自己的提供程序并在那里实现保存。 Here (Basic sample of Entity Framework custom provider) 是如何操作的示例。
假设 appsettings.json 有一个 eureka 端口,并且想在 args (-p 5090) 中动态更改它。通过这样做,可以在创建许多服务时轻松地为 docker 更改端口。
"eureka": {
"client": {
"serviceUrl": "http://10.0.0.101:8761/eureka/",
"shouldRegisterWithEureka": true,
"shouldFetchRegistry": false
},
"instance": {
"port": 5000
}
}
public class Startup
{
public static string port = "5000";
public Startup(IConfiguration configuration)
{
configuration["eureka:instance:port"] = port;
Configuration = configuration;
}
public static void Main(string[] args)
{
int port = 5000;
if (args.Length>1)
{
if (int.TryParse(args[1], out port))
{
Startup.port = port.ToString();
}
}
}
在运行时更新 ASP.NET 核心中的 appsettings.json
文件。
获取此示例 appsettings.json
文件:
{
Config: {
IsConfig: false
}
}
这是将 IsConfig
属性 更新为 true 的代码:
Main()
{
AddOrUpdateAppSetting("Config:IsConfig", true);
}
public static void AddOrUpdateAppSetting<T>(string key, T value)
{
try
{
var filePath = Path.Combine(AppContext.BaseDirectory, "appSettings.json");
string json = File.ReadAllText(filePath);
dynamic jsonObj = Newtonsoft.Json.JsonConvert.DeserializeObject(json);
var sectionPath = key.Split(":")[0];
if (!string.IsNullOrEmpty(sectionPath))
{
var keyPath = key.Split(":")[1];
jsonObj[sectionPath][keyPath] = value;
}
else
{
jsonObj[sectionPath] = value; // if no sectionpath just set the value
}
string output = Newtonsoft.Json.JsonConvert.SerializeObject(jsonObj, Newtonsoft.Json.Formatting.Indented);
File.WriteAllText(filePath, output);
}
catch (ConfigurationErrorsException)
{
Console.WriteLine("Error writing app settings");
}
}
public static void SetAppSettingValue(string key, string value, string appSettingsJsonFilePath = null)
{
if (appSettingsJsonFilePath == null)
{
appSettingsJsonFilePath = System.IO.Path.Combine(System.AppContext.BaseDirectory, "appsettings.json");
}
var json = System.IO.File.ReadAllText(appSettingsJsonFilePath);
dynamic jsonObj = Newtonsoft.Json.JsonConvert.DeserializeObject<Newtonsoft.Json.Linq.JObject>(json);
jsonObj[key] = value;
string output = Newtonsoft.Json.JsonConvert.SerializeObject(jsonObj, Newtonsoft.Json.Formatting.Indented);
System.IO.File.WriteAllText(appSettingsJsonFilePath, output);
}
我正在使用我自己的配置部分和我自己的强类型对象。我总是用这个强类型对象注入 IOptions。而且我能够在运行时更改配置。要非常小心对象的范围。新的配置值由请求范围的对象选取。我正在使用构造函数注入。
关于这方面的文档还很不清楚。我不确定这是否是故意的。阅读此 in-depth discussion
我获取了 Qamar Zamans 的代码(谢谢)并对其进行了修改以允许编辑 more:than:one:layer:deep 的参数。
希望它能帮到一些人,惊讶的是这不是某处的图书馆功能。
public static class SettingsHelpers
{
public static void AddOrUpdateAppSetting<T>(string sectionPathKey, T value)
{
try
{
var filePath = Path.Combine(AppContext.BaseDirectory, "appsettings.json");
string json = File.ReadAllText(filePath);
dynamic jsonObj = Newtonsoft.Json.JsonConvert.DeserializeObject(json);
SetValueRecursively(sectionPathKey, jsonObj, value);
string output = Newtonsoft.Json.JsonConvert.SerializeObject(jsonObj, Newtonsoft.Json.Formatting.Indented);
File.WriteAllText(filePath, output);
}
catch (Exception ex)
{
Console.WriteLine("Error writing app settings | {0}", ex.Message);
}
}
private static void SetValueRecursively<T>(string sectionPathKey, dynamic jsonObj, T value)
{
// split the string at the first ':' character
var remainingSections = sectionPathKey.Split(":", 2);
var currentSection = remainingSections[0];
if (remainingSections.Length > 1)
{
// continue with the procress, moving down the tree
var nextSection = remainingSections[1];
SetValueRecursively(nextSection, jsonObj[currentSection], value);
}
else
{
// we've got to the end of the tree, set the value
jsonObj[currentSection] = value;
}
}
有一个更简单的答案可以在运行时修改 appsettings.json。
Json File structure
var filePath = Path.Combine(System.AppContext.BaseDirectory, "appSettings.json");
string jsonString = System.IO.File.ReadAllText(filePath);
//use https://json2csharp.com/ to create the c# classes from your json
Root root = JsonSerializer.Deserialize<Root>(jsonString);
var dbtoadd = new Databas()
{
Id = "myid",
Name = "mynewdb",
ConnectionString = ""
};
//add or change anything to this object like you do on any list
root.DatabaseSettings.Databases.Add(dbtoadd);
//serialize the new updated object to a string
string towrite = JsonSerializer.Serialize(root);
//overwrite the file and it wil contain the new data
System.IO.File.WriteAllText(filePath, towrite);
根据 Qamar Zaman 和 Alex Horlock 的代码,我稍微修改了一下。
public static class SettingsHelpers
{
public static void AddOrUpdateAppSetting<T>(T value, IWebHostEnvironment webHostEnvironment)
{
try
{
var settingFiles = new List<string> { "appsettings.json", $"appsettings.{webHostEnvironment.EnvironmentName}.json" };
foreach (var item in settingFiles)
{
var filePath = Path.Combine(AppContext.BaseDirectory, item);
string json = File.ReadAllText(filePath);
dynamic jsonObj = Newtonsoft.Json.JsonConvert.DeserializeObject(json);
SetValueRecursively(jsonObj, value);
string output = Newtonsoft.Json.JsonConvert.SerializeObject(jsonObj, Newtonsoft.Json.Formatting.Indented);
File.WriteAllText(filePath, output);
}
}
catch (Exception ex)
{
throw new Exception($"Error writing app settings | {ex.Message}", ex);
}
}
private static void SetValueRecursively<T>(dynamic jsonObj, T value)
{
var properties = value.GetType().GetProperties();
foreach (var property in properties)
{
var currentValue = property.GetValue(value);
if (property.PropertyType.IsPrimitive || property.PropertyType == typeof(string) || property.PropertyType == typeof(decimal))
{
if (currentValue == null) continue;
try
{
jsonObj[property.Name].Value = currentValue;
}
catch (RuntimeBinderException)
{
jsonObj[property.Name] = new JValue(currentValue);
}
continue;
}
try
{
if (jsonObj[property.Name] == null)
{
jsonObj[property.Name] = new JObject();
}
}
catch (RuntimeBinderException)
{
jsonObj[property.Name] = new JObject(new JProperty(property.Name));
}
SetValueRecursively(jsonObj[property.Name], currentValue);
}
}
}
我解决这个问题的方法是添加一个“覆盖”属性 存储在内存缓存中。因此,例如,我的应用程序在“appSettings.json”文件中有一个“CacheEnabled”设置,用于确定是否缓存数据查询结果。在应用程序/数据库测试期间,有时需要将此 属性 设置为“false”。
通过管理员菜单,管理员可以覆盖“CacheEnabled”设置。确定缓存是否启用的逻辑首先检查覆盖。如果找不到覆盖值,则使用“appSettings.json”值。
考虑到实施它需要额外的基础设施,对于很多人来说这可能不是一个好的解决方案。但是,我的应用程序已经有一个缓存服务和一个管理员菜单,所以它很容易实现。
我看到大多数答案都使用 Newtonsoft.Json
包来更新设置。如果您需要更新一层深的设置,您可以不使用 Newtonsoft.Json
并使用 System.Text.Json
(内置于 .Net Core 3.0 及更高版本)功能。这是一个简单的实现:
public void UpdateAppSetting(string key, string value)
{
var configJson = File.ReadAllText("appsettings.json");
var config = JsonSerializer.Deserialize<Dictionary<string, object>>(configJson);
config[key] = value;
var updatedConfigJson = JsonSerializer.Serialize(config, new JsonSerializerOptions { WriteIndented = true });
File.WriteAllText("appsettings.json", updatedConfigJson);
}
在我的项目中,我以这种方式使用 Active Directory 设置:
//...
public class Startup
{
public void ConfigureServices(IServicesCollection services)
{
//...
services.Configure<Ldap>(opts=> {
opts.Url = "example.com";
opts.UseSsl = true;
opts.Port = 111;
opts.BindDn = "CN=nn,OU=nn,OU=nn,DC=nn,DC=nn";
opts.BindCredentials = "nn";
opts.SearchBase = "DC=nn,DC=nn";
opts.SearchFilter = "(&(objectClass=User){0})";
opts.AdminCn = "CN=nn,OU=nn,OU=nn,DC=nn,DC=nn";
opts.SearchGroupBase = "OU=nn,DC=nn,DC=nn";
});
//...
}
}
因此,不使用 appsettings.json。
之后我可以从控制器更新此设置:
//...
[HttpPost("setActiveDirectorySettings")]
public ActionResult<IOptions<Ldap>> SetActiveDirectorySettings(ActiveDirectorySettings clientActiveDirectorySettings)
{
LdapOptions.Value.Url = clientActiveDirectorySettings.Url;
LdapOptions.Value.UseSsl = clientActiveDirectorySettings.UseSsl;
LdapOptions.Value.Port = clientActiveDirectorySettings.Port;
LdapOptions.Value.BindDn = clientActiveDirectorySettings.BindDn;
LdapOptions.Value.BindCredentials = clientActiveDirectorySettings.BindCredentials;
LdapOptions.Value.SearchBase = clientActiveDirectorySettings.SearchBase;
LdapOptions.Value.SearchFilter = clientActiveDirectorySettings.SearchFilter;
LdapOptions.Value.AdminCn = clientActiveDirectorySettings.AdminCn;
LdapOptions.Value.SearchGroupBase = clientActiveDirectorySettings.SearchGroupBase;
return Ok(LdapOptions.Value);
}
//...
看起来对我有用
基于@Alper Ebicoglu 的回答
获取:
// ===== || GET || GET appsettings.js property =====================================================================
[HttpGet]
[Route("GetNotificationDays")]
public async Task<IActionResult> GetNotificationDays()
{
var path = System.IO.Path.Combine(Directory.GetCurrentDirectory(), "appsettings.json");
var json = await System.IO.File.ReadAllTextAsync(path);
dynamic jsonObj = Newtonsoft.Json.JsonConvert.DeserializeObject<Newtonsoft.Json.Linq.JObject>(json);
return StatusCode(200, new { daysBefore = (int)jsonObj.InvoicementNotificationSettings.DaysBefore});
}
Exp:
(int)jsonObj.InvoicementNotificationSettings.DaysBefore =
(int) = cast to int - depending on the property jsonObj = appsettings.js, InvoicementNotificationSettings = object in appsettings.js, DaysBefore = property in InvoicementNotificationSettings
更新:appsettings.js
// ===== || PUT || UPDATE appsettings.js property =====================================================================
[HttpPut]
[Route("SetNotificationDays")]
public async Task<IActionResult> SetNotificationDays(int notificationDays)
{
if (notificationDays != 0)
{
var path = System.IO.Path.Combine(Directory.GetCurrentDirectory(), "appsettings.json");
var json = await System.IO.File.ReadAllTextAsync(path);
dynamic jsonObj = Newtonsoft.Json.JsonConvert.DeserializeObject<Newtonsoft.Json.Linq.JObject>(json);
jsonObj.InvoicementNotificationSettings.DaysBefore = notificationDays;
string output = Newtonsoft.Json.JsonConvert.SerializeObject(jsonObj, Newtonsoft.Json.Formatting.Indented);
await System.IO.File.WriteAllTextAsync(path, output);
return await GetNotificationDays();
}
return StatusCode(409);
}
如果从内存中读取应用程序设置:
例如:int daysBefore = configuration.GetValue<int>("InvoicementNotificationSettings:DaysBefore");
比 Startup.js - 更新后自动重新加载 appsettings.js
public class Startup
{
public static IConfiguration Configuration { get; set; }
// Constructor -----------------------------------------------------------------------------------------------------------------------------
public Startup(IConfiguration configuration, Microsoft.Extensions.Hosting.IHostEnvironment env)
{
Configuration = configuration;
// To autoreload appsettings.js after update -------------------------
var builder = new ConfigurationBuilder()
.SetBasePath(env.ContentRootPath)
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true)
.AddEnvironmentVariables();
Configuration = builder.Build();
}
appsettings.js
{
"ConnectionStrings": {
"DefaultConnection": "Data Source=(localdb)\MSSQLLocalDB;Initial Catalog=ItlCrmsDb;Integrated Security=True;Connect Timeout=30;Encrypt=False;TrustServerCertificate=False;ApplicationIntent=ReadWrite;MultiSubnetFailover=False"
},
"InvoicementNotificationSettings": {
"DaysBefore": 4
},
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
},
"AllowedHosts": "*"
}