elementary analysis. Configuration in netcore

Posted by meomike2000 on Sun, 26 Dec 2021 02:15:21 +0100

Whatever it is Still. Www. 18fu netcore project, we all have to read the configuration file in net, the configuration is usually stored on the web Config, but in The new project in netcore can't see the web at all Config instead of appsetting json.

Create a new webapi project. You can see an IConfiguration in startup. Instantiate it with the constructor through the IOC provided by the framework. In IConfiguration, we found that appsetting can be read directly The configuration item in JSON. If the configuration needs to be read in the controller, it is also directly constructed

Function can instantiate the IConfiguration object to read the configuration. Now let's try the effect of running in appsetting JSON adds a configuration item that can be accessed in action.

 

Add additional profiles

Can our configuration items only be written in appsetting What about JSON? Of course not. Let's see how to add other files to the configuration item. According to the official website tutorial, we can use ConfigureAppConfiguration to implement it.

First, we create a new config. Config in the root directory of the project JSON, and then add several configurations at will, and then in program Add the highlighted part in CS, so that we can simply add the configuration in the newly added JSON file, and then use IConfiguration to access config. In the program json

Configuration item in.

public class Program
{
    public static void Main(string[] args)
    {
        CreateHostBuilder(args).Build().Run();
    }

    public static IHostBuilder CreateHostBuilder(string[] args) =>
        Host.CreateDefaultBuilder(args)
            .ConfigureAppConfiguration(configure => {               
                configure.AddJsonFile("config.json");  //Cannot hot modify

            })
            .ConfigureWebHostDefaults(webBuilder =>
            {
                webBuilder.UseStartup<Startup>();
            });
}

However, the latest data cannot be read after the configuration item is modified. We can call the overload method configure Addjsonfile ("config. JSON", true, reloadonchange: true) implements hot update.

In addition to adding json files, we can also use AddXmlFile to add xml files and AddInMemoryCollection to add memory configurations

public static IHostBuilder CreateHostBuilder(string[] args) =>
    Host.CreateDefaultBuilder(args)
        .ConfigureAppConfiguration((context,configure) => {
            Dictionary<string, string> memoryConfig = new Dictionary<string, string>();
            memoryConfig.Add("memoryKey1", "m1");
            memoryConfig.Add("memoryKey2", "m2");
    
            configure.AddInMemoryCollection(memoryConfig);
    
        })
        .ConfigureWebHostDefaults(webBuilder =>
        {
            webBuilder.UseStartup<Startup>();
        });

Source code interpretation:

In the automatically generated project, we have not configured how to obtain the configuration file How does the netcore framework know to appsetting What about the JSON configuration file? We can understand the truth by looking at the source code.

Host.CreateDefaultBuilder(args)    
    .ConfigureWebHostDefaults(webBuilder =>
    {
        webBuilder.UseStartup<Startup>();
    });


//CreateDefaultBuilder source code
public static IHostBuilder CreateDefaultBuilder(string[] args)
{
    var builder = new HostBuilder();

    builder.UseContentRoot(Directory.GetCurrentDirectory());
    builder.ConfigureHostConfiguration(config =>
    {
        config.AddEnvironmentVariables(prefix: "DOTNET_");
        if (args != null)
        {
            config.AddCommandLine(args);
        }
    });

    builder.ConfigureAppConfiguration((hostingContext, config) =>
    {
        var env = hostingContext.HostingEnvironment;

        config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
              .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: true);

        if (env.IsDevelopment() && !string.IsNullOrEmpty(env.ApplicationName))
        {
            var appAssembly = Assembly.Load(new AssemblyName(env.ApplicationName));
            if (appAssembly != null)
            {
                config.AddUserSecrets(appAssembly, optional: true);
            }
        }

        config.AddEnvironmentVariables();

        if (args != null)
        {
            config.AddCommandLine(args);
        }
    });
    
    ....

    return builder;
}

 

What about? Isn't it interesting? In the source code, we see that when building the host in the Program, we will call ConfigureAppConfiguration to configure the application and read appsetting JSON file, and the Appsettings of different environments will be loaded according to the environment variables. At the same time, you can see that the application not only adds

appsetting configuration, and the support of passing in parameters from environment variables and command line is added. AddUserSecrets supports reading the configuration in the user secret file, which will be used when storing the user secret configuration.

Then why can we call ConfigureAppConfiguration again to append configuration information instead of overwrite it? We can also find the answer in the source code.

public static void Main(string[] args)
{
    CreateHostBuilder(args).Build().Run();
}

//The following is part of the source code
private List<Action<HostBuilderContext, IConfigurationBuilder>> _configureAppConfigActions = new List<Action<HostBuilderContext, IConfigurationBuilder>>();
private IConfiguration _appConfiguration;
public IHostBuilder ConfigureAppConfiguration(Action<HostBuilderContext, IConfigurationBuilder> configureDelegate) { _configureAppConfigActions.Add(configureDelegate ?? throw new ArgumentNullException(nameof(configureDelegate))); return this; } public IHost Build() { ... BuildAppConfiguration(); ... } private void BuildAppConfiguration() { var configBuilder = new ConfigurationBuilder() .SetBasePath(_hostingEnvironment.ContentRootPath) .AddConfiguration(_hostConfiguration, shouldDisposeConfiguration: true); foreach (var buildAction in _configureAppConfigActions) { buildAction(_hostBuilderContext, configBuilder); } _appConfiguration = configBuilder.Build(); _hostBuilderContext.Configuration = _appConfiguration; }

The framework declares a list < action < hostbuildercontext, iconfigurationbuilder > >. Every time we call ConfigureAppConfiguration, we add elements to the collection. When the Main function calls Build, it will trigger ConfigurationBuilder to add all elements in the collection circularly

In the configuration builder, the IConfiguration we use is finally formed How IConfiguration comes from netcore is very clear.

 

Read hierarchy configuration item

What should I do if I need to read the configuration of a certain level in the configuration file? It is also very simple. Use IConfiguration["key:childKey...]

For example, there is a configuration:

"config_key2": {
    "config_key2_1": "config_key2_1",
    "config_key2_2": "config_key2_2"
  }

You can use IConfiguration["config_key2:config_key2_1"] to get config_ key2_ 1, if config_ key2_ There is also a child configuration item childkey under 1, which can still be obtained by using childkey

 

Get configuration item in option mode

The option mode uses classes to provide strongly typed access to relevant configuration sections, and converts the configuration items in the configuration file into POCO model, which can provide better convenience and readability for developers to write code

We add the following configuration:

"Student": {
    "Sno": "SNO",
    "Sname": "SNAME",
    "Sage": 18,
    "ID": "001"
  }

Next, we define a Student's Model class

public class Student
{
    private string _id;
    public const string Name = "Student";
    private string ID { get; set; }
    public string Sno { get; set; }
    public string Sname { get; set; }
    public int Sage { get; set; }
}

There are several ways to bind:

1. Bind using bind

Student student = new Student();
_configuration.GetSection(Student.Name).Bind(student);

2. Bind using Get

var student1 = _configuration.GetSection(Student.Name).Get<Student>(binderOptions=> {
  binderOptions.BindNonPublicProperties = true;
});

Both Get and bind support adding an overload of action < binderoptions >. Through this, we can configure some configurations during binding, such as whether non-public attributes are bound (not bound by default). However, Get is a little more convenient than bind. If you need to bind a collection, it is the same. Let Student

Replace with list < student >.

3. Global mode binding

The first two methods can only take effect locally. If you want to configure binding once, it can take effect anywhere. We can use global binding. Global binding is in startup Defined in CS

services.Configure<Student>(Configuration.GetSection(Student.Name));

This method will use the option mode to configure the binding, and will be injected into the IOC. It can be instantiated in the constructor where it needs to be used

private readonly ILogger<WeatherForecastController> _logger;
private readonly IConfiguration _configuration;
private readonly Student _student;

public WeatherForecastController(ILogger<WeatherForecastController> logger, IConfiguration configuration, IOptions<Student> student)
{
    _logger = logger;
    _configuration = configuration;
    _student = student.Value;
}

 

Use of naming options

When different configuration sections contain the same configuration items, we do not need to define and inject two classes when using option binding. We can specify the name when binding, such as the following configuration:

"Root": {
  "child1": {
    "child_1": "child1_1",
    "child_2": "child1_2"
  },
  "child2": {
    "child_1": "child2_1",
    "child_2": "child2_2"
  }
}

public class Root
{
  public string child_1{get;set;}
  public string child_2{get;set;}
} services.Configure
<Root>("Child1",Configuration.GetSection("Root:child1")); services.Configure<Root>("Child2", Configuration.GetSection("Root:child2")); private readonly ILogger<WeatherForecastController> _logger; private readonly IConfiguration _configuration; private readonly Student _student; private readonly Root _child1; private readonly Root _child2; public WeatherForecastController(ILogger<WeatherForecastController> logger, IConfiguration configuration, IOptions<Student> student,IOptionsSnapshot<Root> root) { _logger = logger; _configuration = configuration; _student = student.Value; _child1 = root.Get("Child1"); _child2 = root.Get("Child2"); }

 

Topics: .NET