Reading configuration data

Source: Internet
Author: User

Reading configuration data

Referring to the word "configuration", I think most of it. NET developers in mind will immediately emerge two special files, that is we are familiar with the app. Config and Web. config, for many years we have been accustomed to the structure of the configuration in the two files. In the era of. NET core, many of the things we take for granted have changed, including the way you define configurations. In general, the new configuration system appears to be more lightweight and more scalable, and its greatest feature is the support for diverse data sources. We can use the memory variables as the configured data source, or we can directly configure the persisted files or even the database. Since a lot of people have never been in touch with this new design of the configuration system, in order to let everyone have a general understanding of it, we first from the programming point of view to experience a new configuration read. This new configuration system defines a very simple API for configured reads that involve three core objects, which we might call " configuring three elements of a programming model ." [This article has been synced to the "ASP. NET Core Framework"]

Directory
First, the configuration programming model three elements
Second, read the configuration in the form of key-value pairs
Third, read the structured configuration
Iv. binding a structured configuration directly to an object

first, the configuration programming model three elements

In terms of programming, this configuration system for. NET core consists of three core objects as shown. The read-out configuration information is eventually converted into a single Configuration object for our program to use. Configurationbuilder is the builder of a configuration object, while Configurationsource represents the most primitive source.

When reading the configuration, we create the corresponding configurationsource based on the configuration definition and register it on the created Configurationbuilder object. Since there may be more than one original source for provisioning, we can register multiple Configurationsource objects of the same or different types to configurationbuilder. Configurationbuilder This is the use of the original data provided by these Configurationsource to build the configuration objects that we use in the program.

According to the usual naming method in this series, we should know that the configuration, Configurationsource and Configurationbuilder described above are collectively referred to as a class of objects, They are represented at the API level by corresponding interfaces (IConfiguration, Iconfigurationsource, and Iconfigurationbuilder), which are defined in the NuGet package "Microsoft. Extensions.Configuration.Abstractions"in. If we only need to use these interfaces in our program, we just need to add a dependency on this NuGet package. As for the default implementation types of these interfaces, they are mostly defined in the NuGet package "Microsoft.Extensions.Configuration".

Second, read the configuration in the form of key-value pairs

Although the configuration in most cases has a structured secondary relationship, the "atomic" configuration items are represented in the simplest "key-value pairs", and the keys and values are usually strings, and then we will demonstrate how to read the configuration in the form of a key-value pair using a simple example. We create a console app for ASP. NET core and add a dependency on the NuGet package for "Microsoft.Extensions.Configuration" in Project.json as follows, The configuration model is implemented in this package.

   1: {
   2: ...   
   3:   "dependencies": {
   4: "     Microsoft.Extensions.Configuration": "1.0.0"
   5:   },
   6:}

Assuming that our application needs to be configured to set the date/time display format, we define the relevant configuration information in this Datetimeformatoptions class as shown below, with its four properties representing four display formats for the DateTime object (long Date/ Time and Short date/time).

   1:public class Datetimeformatoptions
   2: {
   3: Public     string Longdatepattern {get; set;}
   4: Public     string Longtimepattern {get; set;}
   5: Public     string ShortDatePattern {get; set;}
   6: Public     string Shorttimepattern {get; set;}
   7:     //other Members
   8:}

We want to control the date/time display format represented by the four properties of datetimeformatoptions in the form of configuration, so we define a constructor for it. As shown in the following code snippet, the constructor has an IConfiguration interface type parameter, as described above, which we know is the configuration embodied in the application. A key-value pair is a basic representation of the configuration, so the Config object provides an index that allows us to get the value of the configuration item based on the key of the configuration item, and the following code formally calls the index to get the corresponding configuration information.

   1:public class Datetimeformatoptions
   2: {
   3:     //other Members
   4: Public     datetimeformatoptions (iconfiguration config)
   5:     {
   6: This         . Longdatepattern     = config ["Longdatepattern"];
   7: This         . Longtimepattern     = config ["Longtimepattern"];
   8: This         . ShortDatePattern    = config ["ShortDatePattern"];
   9: This         . Shorttimepattern    = config ["Shorttimepattern"];
  Ten:     }
  11:}

To create a Datetimeformatoptions object that embodies the current configuration, we have to get to the configuration object that hosts the relevant config information. As we said above, the configuration object is created by Configurationbuilder, and the original config information is extracted by the corresponding configurationsource. So the correct way to programmatically create a configuration object is to first create a Configurationbuilder object and then register one or more Configurationsource objects for it. Finally, we use Configurationbuilder to create the configuration objects we need.

We use the following program to read the configuration and convert it to a Datetimeformatoptions object. For simplicity, we take a configurationsource of type Memoryconfigurationsource , which directly uses a Dictionary object that is stored in memory as the original configuration source. As shown in the following code snippet, we have set four types of date/time display formats in the Dictionary object provided for Memoryconfigurationsource.

   1:dictionary<string, string> Source = new Dictionary<string, string>
   2: {
   3:     ["longdatepattern"] = "dddd, MMMM D, yyyy",
   4:     ["longtimepattern"] = "H:mm:ss tt",
   5:     ["shortdatepattern"] = "m/d/yyyy",
   6:     ["shorttimepattern"] = "h:mm tt"
   7:};
   
   9:iconfiguration config = new Configurationbuilder ()
  Ten:     . ADD (new Memoryconfigurationsource {initialdata = source})
  One:     . Build ();
  
  13:datetimeformatoptions options = new datetimeformatoptions (config);
  14:console.writeline ($ "longdatepattern: {options. Longdatepattern} ");
  15:console.writeline ($ "longtimepattern: {options. Longtimepattern} ");
  16:console.writeline ($ "shortdatepattern: {options. ShortDatePattern} ");
  17:console.writeline ($ "shorttimepattern: {options. Shorttimepattern} ");

We created an object of type Configurationbuilder and registered the Memoryconfigurationsource with it. Next, we directly call Configurationbuilder's build method to create the configuration object and use the latter to create a Datetimeformatoptions object. To verify that the Datetimeformatoptions object is consistent with the original configuration, we print its four properties on the console. After the program runs, the console will produce the output as shown below.

   1:LONGDATEPATTERN:DDDD, MMMM D, yyyy
   2:LONGTIMEPATTERN:H:MM:SS TT
   3:shortdatepattern:m/d/yyyy
   4:SHORTTIMEPATTERN:H:MM TT

Third, read the structured configuration

Most of the configurations involved in a real project have a structured hierarchy, so the configuration object has the same structure. Structured configuration has a tree hierarchy, which we might call a "configuration tree", a configuration object that eventually corresponds to a node in the tree, and the entire configuration tree can naturally be represented by the root node's corresponding configuration object. Atomic configuration items, which are represented by key-value pairs, typically correspond to "leaf nodes" in the configuration tree that do not have child nodes.

Next we also demonstrate how to define and read a hierarchical configuration as an example. We still use the previous section of the scenario, but now we don't just need to format the date/time, we also need to format other data types, such as the decimal type that represents the currency. For this we define the following Currencydecimalformatoptions class, whose properties digits and symbol represent the decimal place and the currency symbol, respectively. A Currencydecimalformatoptions object is still created with a configuration object.

   1:public class Currencydecimalformatoptions
   2: {
   3: public     int        Digits {get; set;}
   4: Public     string     Symbol {get; set;}
   
   6: Public     currencydecimalformatoptions (iconfiguration config)
   7:     {
   8: This         . Digits = Int. Parse (config["Digits"]);
   9: This         . Symbol = config["symbol"];
  Ten:     }
  11:}

We have defined another type named Formatoptions to represent formatting for different data types. As shown in the following code snippet, its two properties datetime and currencydecimal represent formatting for date/time and currency numbers, respectively. Formatoptions still has a constructor with a parameter type of IConfiguration interface, and its two properties are initialized in this constructor. It is important to note that the initialization of these two properties takes the "sub-configuration section" of the current configuration, and we call the GetSection method by specifying the configuration section name to obtain the two sub-configuration sections.

   1:public class Formatoptions
   2: {
   3: Public     datetimeformatoptions            DateTime {get; set;}
   4: Public     currencydecimalformatoptions     currencydecimal {get; set;}
   
   6: Public     formatoptions (iconfiguration config)
   7:     {
   8: This         . DateTime = new Datetimeformatoptions (config. GetSection ("DateTime"));
   9: This         . Currencydecimal = new Currencydecimalformatoptions (config. GetSection ("Currencydecimal"));
  Ten:     }
  11:}

The configuration represented by the Formatoptions type has a hierarchical structure of trees as shown. In the example we demonstrated above, we provide the original configuration information by using a Memoryconfigurationsource object. Since the original configuration information is a collection of elements of type keyvaluepair<string, string>, which does not have a hierarchical hierarchy of trees on the physical storage, how can it ultimately provide a structured configuration object?

The solution is actually very simple, for a complete configuration tree, the specific configuration information is ultimately carried through the leaf node, so memoryconfigurationsource only need to save the leaf node data in the configuration dictionary. In addition, in order to describe the structure of the configuration tree, the configuration dictionary requires the path of the tree that corresponds to the leaf node as key. So memoryconfigurationsource can use the configuration dictionary shown in the following table to "flatten" the number of configurations, with the path using a colon (":") as the delimiter.

Key

Value

Format:DateTime:LongDatePattern

dddd, MMMM D, yyyy

Format:DateTime:LongT Imepattern

H:mm:ss tt

Format:DateTime:ShortDatePattern

m/d/yyyy

Format:DateTime:ShortTimePattern

h:mm tt

Format:CurrencyDecimal:Digits

2

Format:CurrencyDecimal:Symbol

$

As shown in the following code snippet, we created a dictionary<string, string> object with the structure shown in table 1, and used it to create a Memoryconfigurationsource object. After using CONFIGURATIONBUILDR to obtain a configuration object representing the entire configuration, we call its GetSection method to get the config section with the name "Format" and use the latter to create a formatoptions.

   1:dictionary<string, string> Source = new Dictionary<string, string>
   2: {
   3:     ["format:dateTime:longDatePattern"] = "dddd, MMMM D, yyyy",
   4:     ["format:dateTime:longTimePattern"] = "H:mm:ss tt",
   5:     ["format:dateTime:shortDatePattern"] = "m/d/yyyy",
   6:     ["format:dateTime:shortTimePattern"] = "h:mm tt",
   
   8:     ["format:currencyDecimal:digits"] = "2",
   9:     ["format:currencyDecimal:symbol"] = "$",
  10:};
  11:iconfiguration configuration = new Configurationbuilder ()
  A:         . ADD (new Memoryconfigurationsource {initialdata = source})
  :         . Build ();
  
  15:formatoptions options = new Formatoptions (configuration. GetSection ("Format"));
  16:datetimeformatoptions dateTime = options. DateTime;
  17:currencydecimalformatoptions currencydecimal = options. Currencydecimal;
  
  19:console.writeline ("DateTime:");
  20:console.writeline ($ "\tlongdatepattern: {datetime.longdatepattern}");
  21:console.writeline ($ "\tlongtimepattern: {datetime.longtimepattern}");
  22:console.writeline ($ "\tshortdatepattern: {datetime.shortdatepattern}");
  23:console.writeline ($ "\tshorttimepattern: {datetime.shorttimepattern}");        
  
  25:console.writeline ("Currencydecimal:");
  26:console.writeline ($ "\tdigits:{currencydecimal.digits}");
  27:console.writeline ($ "\tsymbol:{currencydecimal.symbol}");

After getting the Formatoptions object created with the read configuration, in order to verify that the object is consistent with the original configuration data, we still print its related properties on the console. After this procedure, the program will render the output as shown below on the console. (S02)

   1:datetime:
   2:         longdatepattern:dddd, MMMM D, yyyy
   3:         longtimepattern:h:mm:ss TT
   4:         shortdatepattern:m/d/yyyy
   5:         shorttimepattern:h:mm TT
   
   7:currencydecimal:
   8:         Digits          : 2
   9:         Symbol          : $

Iv. binding a structured configuration directly to an object

In the real project development process, we tend to like the two instances we demonstrate by creating the appropriate type (e.g., datetimeformatoptions in the demo instance, Currencydecimaloptions and Formatoptions) to define a set of related configuration options (option), we will define the configuration options (option) for these types called option types. In the example shown above, in order to create these encapsulated configuration objects, we are in the form of manual read configuration, if there are too many defined configuration items, read-only configuration item is a very tedious task.

For an object, if we treat its property as its child node, an object also has a tree hierarchy similar to the Configuration object. If we define the configuration based on the structure of an option type, or define the option type based on the configured structure, the attribute members of option type will have a one by one corresponding relationship with a configuration section, so in principle we can automatically bind the configuration information to a specific option object.

The. NET core configuration system uses a programming pattern called "Options pattern" to support binding from the original configuration to the options object. This programming pattern involves an API defined in the "Microsoft.Extensions.Options.ConfigurationExtensions" NuGet package, So we need to add a specific dependency in the Project.json file as follows. In addition, "Options Pattern" involves the use of Di, so we also need to add to the NuGet package "microsoft.extensions
. Dependencyinjection "dependence.

   1: {
   2: ...   
   3:   "dependencies": {
   4: "     Microsoft.Extensions.Options.ConfigurationExtensions"    : "1.0.0",
   5: "     Microsoft.Extensions.DependencyInjection"                : "1.0.0"
   6:   },
   7:}

With the automatic binding mechanism of options pattern, we don't need to read the configuration one by one, so we can put this three options type (Datetimeformatoptions, The constructors for Currencydecimaloptions and formatoptions are all removed, leaving only their property members. In the main method as the entrance to the program, we create the Formatoptions object that represents the formatting in the following way.

   1: ...
   2:formatoptions options = new Servicecollection ()
   3:     . AddOptions ()
   4:     . configure<formatoptions> (config. GetSection ("Format"))
   5:     . Buildserviceprovider ()
   6:     . Getservice<ioptions<formatoptions>> ()
   7:     . Value;

As shown in the code snippet above, we create a Servicecollection object and call the extension method AddOptions registered with the service for the option model. Next we call the Configure method to map the option type Formatoptions to the corresponding configuration object. We finally use this Servicecollection object to generate a serviceprovider, and call its GetService method to get an object of type ioptions<formatoptions>, The latter's Value property returns the Formatoptions object that is bound to the associated configuration.

Reading configuration data

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.