ASP.NET Core appsettings.json update in code

C#Jsonasp.net Core.Net CoreAppsettings

C# Problem Overview


I am currently working on project using asp.net core v1.1, and in my appsettings.json I have:

"AppSettings": {
   "AzureConnectionKey": "***",
   "AzureContainerName": "**",
   "NumberOfTicks": 621355968000000000,
   "NumberOfMiliseconds": 10000,
   "SelectedPvInstalationIds": [ 13, 137, 126, 121, 68, 29 ],
   "MaxPvPower": 160,
   "MaxWindPower": 5745.35
},

I also have class that I use to store them:

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

And DI enabled to use then in Startup.cs:

services.Configure<AppSettings>(Configuration.GetSection("AppSettings"));

Is there any way to change and save MaxPvPower and MaxWindPower from Controller?

I tried using

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

But it did nothing.

C# Solutions


Solution 1 - C#

Basically you can set the values in IConfiguration like this:

IConfiguration configuration = ...
// ...
configuration["key"] = "value";

The issue there is that e.g. the JsonConfigurationProvider does not implement the saving of the configuration into the file. As you can see in the source it does not override the Set method of ConfigurationProvider. (see source)

You can create your own provider and implement the saving there. Here (Basic sample of Entity Framework custom provider) is an example how to do it.

Solution 2 - C#

Here is a relevant article from Microsoft regarding Configuration setup in .Net Core Apps:

Asp.Net Core Configuration

The page also has sample code which may also be helpful.

Update

I thought In-memory provider and binding to a POCO class might be of some use but does not work as OP expected.

The next option can be setting reloadOnChange parameter of AddJsonFile to true while adding the configuration file and manually parsing the JSON configuration file and making changes as intended.

    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.

Solution 3 - C#

Update appsettings.json file in ASP.NET Core at runtime.

Take this sample appsettings.json file:

{
  Config: {
     IsConfig: false
  }
}

This is the code to update IsConfig property to 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");
    }
}

Solution 4 - C#

I took Qamar Zamans code (thank you) and modified it to allow for editing parameters which are more:than:one:layer:deep.

Hope it helps someone out, surprised that this isn't a library feature somewhere.

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

Solution 5 - C#

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

Solution 6 - C#

According to Qamar Zaman and Alex Horlock codes, I've changed it a little bit.

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


    }
}

Solution 7 - C#

Suppose appsettings.json has an eureka port, and want to change it dynamically in args (-p 5090). By doing this, can make change to the port easily for docker when creating many services.

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

        }
     }

Solution 8 - C#

I'm using my own configuration section and my own strongly typed object. I'm always injecting IOptions with this strongly typed object. And I'm able to change configuration in runtime. Be very careful with scopes of objects. New configuration values are picked up by request scoped object. I'm using constructor injection.

Documentation on this is very unclear though .. I'm no sure if this is meant to be. Read this in-depth discussion

Solution 9 - C#

There is an esier answer to modify the appsettings.json at runtime.

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

Solution 10 - C#

The way I address this issue is by adding an "override" property is stored in a memory cache. So for example, my application has a "CacheEnabled" setting in the "appSettings.json" file that determines whether or not data query results are cached or not. During application / db testing, it is sometimes desirable to set this property to "false".

Through an administrator menu, an Administrator can override the "CacheEnabled" setting. The logic that determines whether or not the Cache is enabled, first checks for the override. If it doesn't find an override value, then it uses the "appSettings.json" value.

This probably isn't a good solution for a lot of people given the extra infrastructure needed to implement it. However, my application already had a Caching Service as well as an Administrator menu, so it was pretty easy to implement.

Solution 11 - C#

In my project I'm work with Active Directory Settings this way:

//...
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";
        });
        //...
    }
}

So, without using appsettings.json.


After that I can update this settings from controller:

//...
    [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);
    }
//...

Looks like it works for me

Solution 12 - C#

Based on @Alper Ebicoglu answer

GET:

// ===== || 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

UPDATE: 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);
    }

If reading appsettings from memmory: Ex: int daysBefore = configuration.GetValue<int>("InvoicementNotificationSettings:DaysBefore");

Than In Startup.js - to autoreload appsettings.js after updating

 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": "*"
}

Solution 13 - C#

I see most of the answers use Newtonsoft.Json package for updating the settings. If you need to update the settings that are one layer deep, you can go without Newtonsoft.Json and use System.Text.Json (built-in on .Net Core 3.0 and above) functionality. Here's a simple implementation:

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

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionSiemkoView Question on Stackoverflow
Solution 1 - C#MathiasView Answer on Stackoverflow
Solution 2 - C#AnkitView Answer on Stackoverflow
Solution 3 - C#Qamar ZamanView Answer on Stackoverflow
Solution 4 - C#Alex HorlockView Answer on Stackoverflow
Solution 5 - C#Alper EbicogluView Answer on Stackoverflow
Solution 6 - C#Mahdi FarhaniView Answer on Stackoverflow
Solution 7 - C#user11907192View Answer on Stackoverflow
Solution 8 - C#Mitja GustinView Answer on Stackoverflow
Solution 9 - C#Dani FView Answer on Stackoverflow
Solution 10 - C#Alan FarkasView Answer on Stackoverflow
Solution 11 - C#jeison jeiView Answer on Stackoverflow
Solution 12 - C#Stefan27View Answer on Stackoverflow
Solution 13 - C#Mykhailo SeniutovychView Answer on Stackoverflow