Why do I get an error "value cannot be null (Parameter 'type')" when calling Configuration.Reload()

Harry Ridpath 1 Reputation point
2021-12-08T16:16:11.697+00:00

I am using the example code from https://learn.microsoft.com/en-us/answers/questions/299791/saving-to-appsettingsjson.html.

This code is excellent and writes the appsettings.json perfectly. However, when the _configuration.Reload() is called in the WritableOptions class, I get an multiple errors: (Value cannot be null (Parameter 'type')).

I do not have the word 'type' in my appsettings.json. I do not understand where this is coming from.

Does anyone have any ideas as to where I need to look?

ASP.NET Core
ASP.NET Core
A set of technologies in the .NET Framework for building web applications and XML web services.
4,573 questions
0 comments No comments
{count} votes

3 answers

Sort by: Most helpful
  1. Zhi Lv - MSFT 32,336 Reputation points Microsoft Vendor
    2021-12-09T07:13:02.283+00:00

    Hi @Harry Ridpath ,

    Welcome to Microsoft Q&A forum.

    Can you tell us your application version, whether it is an Asp.net 5 application?

    According to the link, I created an Asp.net 5 MVC application (named CoreMVC5Sample) and tested the related code, everything works well on my side. You can refer to it (please note the package/namespace reference, the issue might relate them): In this sample, I'm using the IWebHostEnvironment and Newtonsoft (in this sample, I use the Newtonsoft.Json 13.0.1 package, you can install it via Nuget).

    [Note] The following code also works well in an Asp.net 5 API application.

    1. create IWritableOptions interface and WritableOptions class:
      using Microsoft.AspNetCore.Hosting;  
      using Microsoft.Extensions.Configuration;  
      using Microsoft.Extensions.Options;  
      using Newtonsoft.Json;  
      using Newtonsoft.Json.Linq;  
      using System;  
      using System.IO;  
      
      namespace CoreMVC5Sample.Services   
      {  
          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();  
              }  
          }  
      }  
      
    2. The ServiceCollectionExtensions class:
      using Microsoft.AspNetCore.Hosting;  
      using Microsoft.Extensions.Configuration;  
      using Microsoft.Extensions.DependencyInjection;  
      using Microsoft.Extensions.Options;  
      
      namespace CoreMVC5Sample.Services  
      {  
          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);  
                  });  
              }  
          }  
      }  
      
    3. The Locations class:
      namespace CoreMVC5Sample.Models  
      {  
          public class Locations  
          {  
              public Locations()  
              {  
                  Name = "default";  
              }  
              public string Name { get; set; }  
          }  
      }  
      
    4. The appsettings.json file: pay attention to the json format. 156097-image.png
    5. Add the service in the startup file:
       //required  using CoreMVC5Sample.Models;     // the Locations class is in the Models folder.  
      // required using CoreMVC5Sample.Services; //the IWritableOptions is in the services folder.  
      
       services.ConfigureWritable<Locations>(Configuration.GetSection("Locations"));  
      
    6. The Controller and View page use the same code:
      public class HomeController : Controller  
      {   
          private readonly IWritableOptions<Locations> _writableLocations;  
      
          public HomeController(IWritableOptions<Locations> writableLocations)  
          {   
              _writableLocations = writableLocations;  
          }  
          public IActionResult Change(string value)  
          {  
              _writableLocations.Update(opt => {  
                  opt.Name = value;  
              });  
              return RedirectToAction("Index");  
          }  
      
      View:
       <form asp-action="Change" method="post">  
           <input name="value" type="text"/>  
           <input type="submit"  value="submit"/>  
       </form>  
      

    The result is like this:

    156203-1.gif

    You can try to use the above steps to create a new sample to test, if still meet the "value cannot be null" error, please create a simple sample to reproduce the problem.


    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


  2. Harry Ridpath 1 Reputation point
    2021-12-10T17:57:25.973+00:00

    Here is my code:

        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<IHostEnvironment>();
                    var options = provider.GetService<IOptionsMonitor<T>>();
                    return new WritableOptions<T>(environment, options, configuration, section.Key, file);
                });
            }
        }
        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 IHostEnvironment _environment;
            private readonly IOptionsMonitor<T> _options;
            private readonly IConfigurationRoot _configuration;
            private readonly string _section;
            private readonly string _file;
    
            public WritableOptions(
                IHostEnvironment 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));
                Thread.Sleep(int.Parse(_configuration["UpdateTuneDelay"]));
                _configuration.Reload();
                EnvironmentJwtConfigList.Refresh();
                EnvironmentResponseList.Refresh();
            }
        }
        public class Endpoints
        {
            public Dictionary<string, Endpoint> Items { get; set; }
            [JsonConstructor]
            public Endpoints( Dictionary<string, Endpoint> items)
            {
                Items = items;
            }
            public Endpoints()
            {
    
            }
            public static void Reload()
            {
                EnvironmentResponseList.Refresh();
            }
        }
        public class Endpoint
        {
            public string Uri { get; set; }
            public Dictionary<string,string> ConnectionStrings { get; set; }
            public Dictionary<string, object> CustomProperties { get; set; }
            [JsonConstructor]
            public Endpoint(string uri, Dictionary<string, string> connectionStrings, Dictionary<string, object> customProperties)
            {
                Uri = uri;
                ConnectionStrings = connectionStrings;
                CustomProperties = customProperties;
            }
            public Endpoint()
            {
    
            }
        }
    

    Appsetting.json Section:

    "Endpoints": {
        "Items": {
        "Environment": {
                "Uri": "http://localhost:50400",
                "ConnectionStrings": {},
                "CustomProperties": {}
            },
            "Support": {
                "Uri": "http://localhost:50411/",
                "ConnectionStrings": {},
                "CustomProperties": {
                    "Plugins": [
                        {
                        "PluginName": "Administration",
                        "AppName": "@supoort/Administration",
                        "ApplicationOrLoadingFn": "",
                        "ActivityFn": "",
                        "CustomProps": "",
                        "ActiveWhen": "/"
                        }
                ]
            }
          }
        }
    }
    
    0 comments No comments

  3. Zhi Lv - MSFT 32,336 Reputation points Microsoft Vendor
    2021-12-13T09:52:15.703+00:00

    Hi @Harry Ridpath ,

    The issue might relate the Dictionary.

    I tried to create a sample using your json string and the Endpoint class. The result is that the appsettings.json file was updated, but it will throw an error "value cannot be null (Parameter 'type')" when calling Configuration.Reload().

    I found a similar thread: New Asp.NET Core 3.0 Json doesn't serialize Dictionary<Key,Value> , but unfortunately, I tested the suggest in this thread, still not found a solution to use the dictionary.

    As a workaround, you could consider modifying the class as below:

    public class Endpoints  
    {  
        public Items Items { get; set; }  
    }  
    public class Plugin  
    {  
        public string PluginName { get; set; }  
        public string AppName { get; set; }  
        public string ApplicationOrLoadingFn { get; set; }  
        public string ActivityFn { get; set; }  
        public string CustomProps { get; set; }  
        public string ActiveWhen { get; set; }  
    }  
      
    public class Support  
    {  
        public string Uri { get; set; }  
        public ConnectionStrings ConnectionStrings { get; set; }  
        public CustomProperties CustomProperties { get; set; }  
    }  
    public class Environment  
    {  
        public string Uri { get; set; }  
        public ConnectionStrings ConnectionStrings { get; set; }  
        public CustomProperties CustomProperties { get; set; }  
    }  
    public class Items  
    {  
        public Environment Environment { get; set; }  
        public Support Support { get; set; }  
    }  
    public class ConnectionStrings  
    {  
    }  
      
    public class CustomProperties  
    {  
        public List<Plugin> Plugins { get; set; }  
    }  
    

    Then change the Interface, class and the ServiceCollectionExtensions to use this class, after that, you could use the following code to modify the appsettings.json file: (Here I named the Interface as _IEndpoints)

            _IEndpoints.Update(opt =>  
            {  
                opt.Items.Environment.CustomProperties.Plugins = new List<Plugin>() { new Plugin()  
                {  
                    PluginName="User"  
                } };  
            });  
    

    The result is like this:

    157154-1.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

    0 comments No comments

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.