How to save the updates I made to appsettings configurations back to the appsettings.json file programatically.

Tim Murphy 1 Reputation point
2021-10-29T11:47:10.817+00:00

I'm using ASP.NET Core 3.1. I have my configuration settings in the appsettings.json file. I can easily read these settings and update the settings in memory. However, I want to save those updates in the appsettings.json file too.

public class MyController : ControllerBase
{
    private readonly IConfiguration _configuration;

    public MyController(ILogger<MyController> logger, ISqlDataAccess sQlDataAccess, IConfiguration configuration)
    {
        _configuration = configuration;
        string myKey = _configuration["MyKey"]; // Reads key from appsettings.json
        myKey = "NewKeyValue";
        _configuration["MyKey"] = myKey ; // Writes new key to appsettings key in memory.
        // Does not save appsettings key in memory back to the appsettings.json file. How can I do this?
    }
}
Developer technologies | ASP.NET | ASP.NET Core
Developer technologies | C#
0 comments No comments
{count} votes

2 answers

Sort by: Most helpful
  1. Anonymous
    2021-11-01T06:34:20.707+00:00

    Hi @Tim Murphy ,

    You can refer the following sample:

    [Note] In this sample, we are using Newtonsoft.Json, you could install it via Nuget.

    Create a IWritableOptions:

    //Required using Microsoft.AspNetCore.Hosting;  
    //Required using Microsoft.Extensions.Configuration;  
    //Required using Microsoft.Extensions.Options;  
    //Required using Newtonsoft.Json;  
    //Required using Newtonsoft.Json.Linq;  
    
    public interface IWritableOptions<out T> : IOptions<T> where T : class, new()  
    {  
        void Update(Action<T> applyChanges);  
    }  
    public class WritableOptions<T> : IWritableOptions<T> where T : class, new()  
    {  
        private readonly IWebHostEnvironment _environment;  
        private readonly IOptionsMonitor<T> _options;  
        private readonly IConfigurationRoot _configuration;  
        private readonly string _section;  
        private readonly string _file;  
    
        public WritableOptions(  
            IWebHostEnvironment environment,  
            IOptionsMonitor<T> options,  
            IConfigurationRoot configuration,  
            string section,  
            string file)  
        {  
            _environment = environment;  
            _options = options;  
            _configuration = configuration;  
            _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));  
            _configuration.Reload();  
        }  
    }  
    

    Then, create a ServiceCollectionExtensions class:

    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 configuration = (IConfigurationRoot)provider.GetService<IConfiguration>();  
                var environment = provider.GetService<IWebHostEnvironment>();  
                var options = provider.GetService<IOptionsMonitor<T>>();  
                return new WritableOptions<T>(environment, options, configuration, section.Key, file);  
            });  
        }  
    }  
    

    Register the service in the ConfigureServices method:

            services.ConfigureWritable<Locations>(Configuration.GetSection("Locations"));  
    

    The appsetting.json file content as below:

    {  
      "ConnectionStrings": {  
        "DefaultConnection": "XXX"  
      },  
      "Logging": {  
        "LogLevel": {  
          "Default": "Information",  
          "Microsoft": "Warning",  
          "Microsoft.Hosting.Lifetime": "Information"  
        }  
      },  
      "Locations": {  
        "Name": "default value"  
      },  
      "AllowedHosts": "*"  
    }  
    

    Assume the Locations's Name property need to update, then, we can create a Locations class as below:

    public class Locations  
    {  
        public Locations()  
        {  
            Name = "default";  
        }  
        public string Name { get; set; }  
    }  
    

    And create a UpdateAppsetting action to update the name.

    public class HomeController : Controller  
    {   
        private readonly IWritableOptions<Locations> _writableLocations;  
        public HomeController(IWritableOptions<Locations> writableLocations)  
        {   
            _writableLocations = writableLocations;  
        }  
        public IActionResult UpdateAppsetting()  
        {  
            _writableLocations.Update(opt => {  
                opt.Name = "Updated";  
            });  
    
            return RedirectToAction(nameof(Index));  
        }  
    

    Code in the Index view page, add a button to trigger the UpdateAppsetting action:

    <a class="btn btn-light" asp-area="" asp-controller="Home" asp-action="UpdateAppsetting">Update Location Name</a>  
    

    Then, the result as below: we can see the value has been changed to "updated".

    145404-2.gif


    If the answer is the right solution, please click "Accept Answer" and kindly upvote it. If you have extra questions about this answer, please click "Comment".
    Note: Please follow the steps in our documentation to enable e-mail notifications if you want to receive the related email notification for this thread.

    Best regards,
    Dillion

    3 people found this answer helpful.
    0 comments No comments

  2. Karen Payne MVP 35,586 Reputation points Volunteer Moderator
    2021-10-29T12:55:50.763+00:00

    You can save changes using either Newtonsoft Json.net or System.Text.Json.

    Helpers to read (only needed here for demo'ing) and write json file

    using System;
    using System.IO;
    using System.Text.Json;
    
    namespace Json.Library.Extensions
    {
        public static class SystemJson
        {
            public static T JSonToItem<T>(this string jsonString) => JsonSerializer.Deserialize<T>(jsonString);
            public static (bool result, Exception exception) JsonToFile<T>(this T sender, string fileName, bool format = true)
            {
                try
                {
                    var options = new JsonSerializerOptions { WriteIndented = true };
                    File.WriteAllText(fileName, JsonSerializer.Serialize(sender, format ? options : null));
    
                    return (true, null);
                }
                catch (Exception exception)
                {
                    return (false, exception);
                }
            }
        }
    }
    

    Sample appsettings file

    {
      "Logging": {
        "LogLevel": {
          "Default": "Warning"
        }
      },
      "AllowedHosts": "*",
      "ConnectionStrings": {
        "DevConnection": "Server=(local)\\sqlexpress;Database=EmployeeDB;Trusted_Connection=True;MultipleActiveResultSets=True;"
      }
    }
    

    Concrete classes

    public class Configuration
    {
        public Logging Logging { get; set; }
        public string AllowedHosts { get; set; }
        public Connectionstrings ConnectionStrings { get; set; }
    }
    
    public class Logging
    {
        public Loglevel LogLevel { get; set; }
    }
    
    public class Loglevel
    {
        public string Default { get; set; }
    }
    
    public class Connectionstrings
    {
        public string DevConnection { get; set; }
    }
    

    Operations (you don't need the read operation)

    public class ConfigurationOperations
    {
        private static string FileName => Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "appsettings.json");
        public static Configuration ReadConfiguration()
        {
            var json = File.ReadAllText(FileName);
            return json.JSonToItem<Configuration>();
        }
    
        public static void SaveChanges(Configuration configuration)
        {
            configuration.JsonToFile(FileName);
        }
    }
    

    Usage

    var configuration = ConfigurationOperations.ReadConfiguration();
    configuration.ConnectionStrings.DevConnection = "Server=.\\sqlexpress;Database=North;Trusted_Connection=True;MultipleActiveResultSets=True;";
    ConfigurationOperations.SaveChanges(configuration);
    
    1 person found this answer helpful.

Your answer

Answers can be marked as Accepted Answers by the question author, which helps users to know the answer solved the author's problem.