Bind a configuration to an object [prev]

Source: Internet
Author: User

Bind a configuration to an object [prev]

For programmatic convenience, we do not normally read the value of a single configuration item directly from the configuration object created by Configurationbuilder, but rather to bind a set of related configurations to an object, which we call the options object. In the first section of this chapter, we demonstrate how the options model can be used to bind the configuration data to the options object by using a simple example, and now we will introduce the implementation principle behind the options model in detail.

Directory
First, Configurationbinder
Second, binding simple objects
Third, binding complex objects
Iv. Binding Collection Objects
V. Binding Dictionary Objects

First, Configurationbinder

The configuration is logically represented as a hierarchical configuration tree, and for an options object, if we treat its data members as child nodes, then the options object also has a hierarchical attribute structure, so the options objects and configurations have no essential differences in the data structure level. If the data member definition of the options type has a one by one matching relationship with the configured structure, then it is easy to bind the latter to a corresponding type of options object. The Configurationbinder we focus on in this section is to implement the automatic binding of a structured configuration to a data object using this principle.

Configurationbinder is a static type that is defined in the assembly "Microsoft.Extensions.Configuration.Binder", and the name of the assembly is also the name of the NuGet package in which it is located. The binding capabilities it provides for configuration are embodied in the bind and a series of get methods that it defines. As shown in the following code snippet, these are extension methods for the IConfiguration interface.

   1:public Static Class Configurationbinder
   2: {
   3: Public     static void Bind (this iconfiguration configuration, object instance);
   
   5: Public     Static Object Get (this iconfiguration configuration, type type);
   6: Public     static Object Get (this iconfiguration configuration, type type, string key);
   7: Public     static T-get<t> (this iconfiguration configuration);
   8: Public     static T-get<t> (this iconfiguration configuration, T defaultvalue);
   9: Public     static T-get<t> (This iconfiguration configuration, string key);
  : Public     static T-get<t> (This iconfiguration configuration, string key, T defaultvalue);
  11:}

We can call the bind method to bind a configuration object to a pre-created object. The Get method, in turn, binds the configuration data hosted by the Config object directly to the object according to the corresponding data object of the specified type, which is determined by the parameter type or the generic parameter types of the method. If a get method with parameter key is called, the configuration of the binding is derived from the sub-configuration section represented by this key.

The target type of a configurationbinder binding can be a simple primitive type or a complex custom data type, or it can be a collection or a dictionary type. We know from the above that the physical structure of the configuration is represented as a two-dimensional data dictionary, so how can the raw data be represented by a set of string-type key-value pairs for the different types of data objects generated by the binding?

Ii. binding simple data types

Because an atomic configuration item is always represented as a KeyValuePair <string,string > object, the original data type of the configuration binding is a string. There is only one defining criterion for the so-called simple data types and complex data types, which is whether data conversions from string types are supported . A simple type object can be converted directly from a string, and a complex type object cannot.

If the target type of the binding is a simple type, it is possible to convert the value of the configuration item (as ConfigurationSection Value property) to the corresponding data type when configuring the binding. Since all primitive types (such as Int32, double, and so on) are simple types, we can bind their values directly as follows.

   1:iconfiguration configuration = new Configurationbuilder ()
   2:     . ADD (New Memoryconfigurationprovider (New dictionary<string, string>
   3:     {
   4:         ["foo"] = "abc",
   5:         ["bar"] = "123",
   6:         ["baz"] = "3.14"
   7:     })). Build ();
   
   9:debug.assert (configuration. GetSection ("foo"). get<string> () = = "abc");
  10:debug.assert (configuration. Get<int> ("bar") = = 123);
  11:debug.assert (configuration. Get<double> ("bar") = = 3.14);

Our custom data type is not a simple type by default, but we can specify a typeconverter for it, and if the latter supports a type conversion from a string, then that type naturally becomes a simple type. As shown in the following code snippet, we define a point type that represents two-dimensional coordinate points. A TypeConverter (Pointtypeconverter) that originates from string type conversions is applied by labeling the TypeConverterAttribute attribute. When configuring bindings, if the original configuration item has a matching format, it can be directly bound to a point object.

   1: [TypeConverter (typeof (Pointtypeconverter))]
   2:public class Point
   3: {
   4: Public     double X {get; set;}
   5: Public     double Y {get; set;}
   6:}
   
   8:public class Pointtypeconverter:typeconverter
   9: {
  Ten: Public     override Object ConvertFrom (ITypeDescriptorContext context, CultureInfo culture, object value)
  One:     {
  :         string[] Split = value. ToString (). Split (', ');
  :         return new Point
  :         {
  A:             X = Double. Parse (Split[0]. TrimStart (' (')),
  x:             Y = Double. Parse (Split[1]. TrimStart (') '))
  :         };
  :     }
  19:}
  
  21:iconfiguration configuration = new Configurationbuilder (). ADD (New Memoryconfigurationprovider (New dictionary<string, string>
  :     {
  :         ["point"] = "(1.2,3.4)"
  (:     })). Build ();
  25:debug.assert (configuration. Get<point> ("point"). X = = 1.2);
  26:debug.assert (configuration. Get<point> ("point"). Y = = 3.4);

Third, binding complex data types

If a complex object is represented by a tree, the real data is hosted by a leaf node, and the data types for the leaf nodes are simple types. If a two-dimensional data dictionary is provided for all the raw data of a complex object, then the dictionary only needs to contain the values corresponding to the leaf nodes. As for how to embody the structure of complex objects through a Dictionary object, we just need to use the path of the leaf node as the key to the dictionary element.

   1:public class Profile
   2: {
   3: Public     Gender         Gender {get; set;}
   4: Public     int age            {get; set;}
   5: Public     contactinfo    contactinfo {get; set;}
   6:}
   
   8:public class ContactInfo
   9: {
  : Public     string Email {get; set;}
  One: Public     string Phoneno {get; set;}
  12:}
  
  14:public enum Gender
  15: {
  :     Male,
  :     Female
  18:}

As shown in the code snippet above, we define a profile class that represents the basic information of the individual, which defines the three attributes (Gender, age, and ContactInfo), respectively, that represent gender, ages, and contact details. The ContactInfo object that represents the contact information has two properties (email and Phoneno) that represent the email address and phone number respectively. A complete profile object can be represented by a tree as shown in the image on the right.

If you need to represent a complete profile object in the form of a configuration, we only need to define the data corresponding to the four leaf nodes (gender, age, email address and phone number) in the configuration. For the data dictionary that hosts the configuration data, we need to use the path of the four leaf nodes as key to the dictionary element as shown in the table on the left.

We demonstrate the configuration bindings for complex objects through a simple example. We create an ASP. NET Core Console app and add a dependency to the NuGet package "Microsoft.Extensions.Configuration.Binder" in the Project.json file. We define the program as shown below in the main method as the entrance to the program.

   1:public class Program
   2: {
   3: Public     static void Main (string[] args)
   4:     {
   5:         iconfiguration configuration = new Configurationbuilder (). ADD (New Memoryconfigurationprovider (New dictionary<string, string>
   6:             {
   7:                 ["Profile:gender"]             = "Male",
   8:                 ["profile:age"]                = "18",
   9:                 ["Profile:ContactInfo:Email"]  = "[Email protected]",
  Ten:                 ["Profile:ContactInfo:PhoneNo"]= "123456789",
  One:             }). Build ();
  
  : Profile Profile         = Configuration. Get<profile> ("Profile");
  :         Console.WriteLine ("{0,-10}:{1}", "Gender", profile. Gender);
  :         Console.WriteLine ("{0,-10}:{1}", "Age", profile. Age);
  :         Console.WriteLine ("{0,-10}:{1}", "Email", profile. Contactinfo.email);
  :         Console.WriteLine ("{0,-10}:{1}", "Phoneno", profile. Contactinfo.phoneno);
  :     }
  19:}

As shown in the code snippet above, we created a Configurationbuilder object and added a memoryconfigurationprovider to it, which provides the original configuration data as shown in table 2. We use the configuration object created by this Configurationbuilder object and call this ConfigurationSection get method to bind the config section of key to "profile" as one. In order to verify that the configuration binding was successful, we eventually printed out information about the binding's profile object. After the program executes, it produces the output shown below on the console.

   1:gender    : Male
   2:age       : 18
   3:email     : [Email protected]
   4:phoneno   : 123456789

Iv. Binding Collection Objects

The configuration binding is essentially the transformation of data that hosts the same information between different structures, and more specifically, the conversion of data from a data dictionary-based physical structure to a tree-based logical structure. To understand the configuration bindings for a collection, you need to first understand how a collection object logically embodies a tree. For a collection object, the elements that make up it are naturally treated as child nodes of the collection, so a collection of three profile objects can be represented by the tree shown in the diagram on the left.

But this tree, as shown in 8, is not a valid configuration tree. For this tree, the configuration section that represents the collection element uses "profile" as the key, which causes the path of all leaf nodes to be not unique. Because the path is not unique, we naturally cannot use it as a key to a Dictionary object, so the atomic data that makes up this collection cannot be hosted by a data dictionary. To solve this problem, we will set the index of the elements (0, 1, 2, ...). ) corresponding to the configuration section key, so the right figure shows the structure of the true configuration tree.

Now that we can properly represent the collection object through a legitimate configuration tree, we can directly use a Dictionary object to provide all the atomic data that makes up this collection object. Each element in the data dictionary corresponds to a leaf node in the configuration tree whose path is directly the key to the dictionary element, and the table below clearly embodies the structure of the data dictionary.

We still use a simple example to demonstrate the configuration bindings for the collection. As shown in the following code snippet, we created a Configurationbuilder object and added a memoryconfigurationprovider to it, which provides the original configuration data in the structure shown in table 3. We use the configuration object created by this Configurationbuilder object and call this ConfigurationSection get method to bind the config section of key "Profiles" to a list< The Profile> object.

   1:public class Program
   2: {
   3: Public     static void Main (string[] args)
   4:     {
   5:         iconfiguration configuration = new Configurationbuilder (). ADD (New Memoryconfigurationprovider (New dictionary<string, string>
   6:             {
   7:                 ["profiles:0:gender"] = "Male",
   8:                 ["profiles:0:age"] = "18",
   9:                 ["profiles:0:contactinfo:email"] = "[Email protected]",
  Ten:                 ["Profiles:0:contactinfo:phoneno"]     = "123",
  
  :                 ["profiles:1:gender"] = "Male",
  :                 ["profiles:1:age"] = "25",
  :                 ["Profiles:1:contactinfo:email"]       = "[Email protected]",
  :                 ["Profiles:1:contactinfo:phoneno"]     = "456",
  
  :                 ["profiles:2:gender"] = "Female",
  :                 ["profiles:2:age"] = "40",
  :                 ["Profiles:2:contactinfo:email"]       = "[Email protected]",
  :                 ["Profiles:2:contactinfo:phoneno"]     = "789",
  (:             })). Build ();
  
  :         ienumerable<profile> profiles = Configuration. Get<list<profile>> ("Profiles");
  : foreach (Profile profiles in         profiles)
  :         {
  
  :             Console.WriteLine ("{0,-10}:{1}", "Gender", profile. Gender);
  :             Console.WriteLine ("{0,-10}:{1}", "Age", profile. Age);
  :             Console.WriteLine ("{0,-10}:{1}", "Email", profile. Contactinfo.email);
  :             Console.WriteLine ("{0,-10}:{1}\n", "Phoneno", profile. Contactinfo.phoneno);
  :         }
  :     }
  33:}

In order to verify that the configuration binding was successful, we eventually printed out information about each element of the bound List<profile> object, which, after execution, produced the output shown below on the console.

   1:gender    : Male
   2:age       : 18
   3:email     : [Email protected]
   4:phoneno   : 123
   
   6:gender    : Male
   7:age       : 25
   8:email     : [Email protected]
   9:phoneno   : 456
  
  11:gender    : Female
  12:age       : 40
  13:email     : [Email protected]
  14:phoneno   : 789

Five, bound dictionary

A dictionary can be considered a collection of element types as key-value pairs, which are very similar in their representation on the configuration tree, where the only difference is that they take an index as the key for the configuration section where the collection element is located, which directly uses key-value pairs as key to the configuration section. As a simple example, we use a dictionary<string, Profile > object to represent the personal information of multiple users, and the user name as the key of this dictionary, then the configuration tree of this Dictionary object will have the structure as shown in the image on the right ("Foo", " Bar "and" Baz "represent the key for the user name).

The configuration tree that represents the collection and the dictionary is basically similar in structure, so the same is reflected in the physical structure based on the data dictionary. For the dictionary<string, profile> object represented by the right graph, all atomic configuration data that constructs the object can be provided by a data dictionary that contains the following elements.

We still use a simple example to illustrate the configuration bindings for dictionaries. As shown in the following code snippet, we created a Configurationbuilder object and added a memoryconfigurationprovider to it, which provides the original configuration data as shown in the table on the left. We use the configuration object created by this Configurationbuilder object and call this ConfigurationSection get method to set key to "Profiles" The configuration section is bound to a dictionary<string, profile> object.

   1:public class Program
   2: {
   3: Public     static void Main (string[] args)
   4:     {
   5:         iconfiguration configuration = new Configurationbuilder (). ADD (New Memoryconfigurationprovider (New dictionary<string, string>
   6:             {
   7:                 ["Profile:Foo:Gender"]               = "Male",
   8:                 ["Profile:Foo:Age"]                  = "18",
   9:                 ["Profile:Foo:ContactInfo:Email"]    = "[Email protected]",
  Ten:                 ["Profile:Foo:ContactInfo:PhoneNo"]  = "123",
  
  :                 ["Profile:Bar:Gender"]               = "Male",
  :                 ["Profile:Bar:Age"]                  = "25",
  :                 ["Profile:Bar:ContactInfo:Email"]    = "[Email protected]",
  :                 ["Profile:Bar:ContactInfo:PhoneNo"]  = "456",
  16:                     
  :                 ["Profile:Baz:Gender"]               = "Female",
  :                 ["Profile:Baz:Age"]                  = "40",
  :                 ["Profile:Baz:ContactInfo:Email"]    = "[Email protected]",
  :                 ["Profile:Baz:ContactInfo:PhoneNo"]  = "789",
  (:             })). Build ();
  
  At:         dictionary<string, profile> profiles = Configuration. Get<dictionary<string, profile>> ("Profile");
  :         foreach (var item in profiles)
  :         {
  
  :             Console.WriteLine ("{0,-10}:{1}", "Name", item. Key);
  :             Console.WriteLine ("{0,-10}:{1}", "Gender", item. Value.gender);
  :             Console.WriteLine ("{0,-10}:{1}", "Age", item. Value.age);
  :             Console.WriteLine ("{0,-10}:{1}", "Email", item. Value.ContactInfo.Email);
  To:             Console.WriteLine ("{0,-10}:{1}\n", "Phoneno", item.) Value.ContactInfo.PhoneNo);
  :         }
  :     }
  34:}

In order to verify that the configuration bindings were successful, we eventually printed out the information about each element of the binding's dictionary<string, Profile> object, which, after execution, produced the output shown below on the console.

   1:name      : Foo
   2:gender    : Male
   3:age       : 18
   4:email     : [Email protected]
   5:phoneno   : 123
   
   7:name      : Bar
   8:gender    : Male
   9:age       : 25
  10:email     : [Email protected]
  11:phoneno   : 456
  
  13:name      : Baz
  14:gender    : Female
  15:age       : 40
  16:email     : [Email protected]
  17:phoneno   : 789

Configuration of ASP (1): Read configuration information
Configuration of ASP. NET Core (2): Configuration model detailed
Configuration of ASP. 3: Bind configuration to Object [prev]
ASP. config (3): Bind configuration to Object [next]
Configuration of ASP (4): A variety of configuration sources [prev]
Configuration of ASP (4): A variety of configuration sources [next]
Configuration of ASP (5): Configured synchronization

Bind a configuration to an object [prev]

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.