The new configuration system uses .NET Core [9]: Why is not good enough for XML support? How to improve?

Physical file is the carrier of the original configuration of our most commonly used, the optimal configuration file format consists of three main, they are JSON, XML and INI, the corresponding configuration source type are JsonConfigurationSource, XmlConfigurationSource and IniConfigurationSource. But for .NET Core configuration system, we take for granted actually is not an ideal configuration XML source, and at least JSON comparison, it has an inherent disadvantage, and that is support for the collection of data structures is not satisfactory. [This article has been synchronized to the " ASP.NET Framework Core Secret " among]

First, why it is difficult to be represented by an elegant collection of XML for configuration

In the " Configuration Model Detailed Design ," a paper we design and implement configuration model are described in detail. In this article we say reflects the application configuration is a tree of hierarchy, as I call it "configuration tree", the specific configuration data by the configuration tree "leaf node" bearer. When the configuration data are converted from different sources after loading into a dictionary, I call it "Configuring the dictionary." In order to "Configure dictionary" able to store "configuration tree" of all the data and its structure, we need to store all leaf nodes in the configuration dictionary, path, and the value of the leaf node directly as Key and Value dictionary elements. Key is unique because the dictionary, which requires a configuration of each node in the tree must have a unique path. XmlConfigurationSource / XmlConfigurationProvider not well supported set of data structures problem arises here.

   1: public class Profile
   2: {
   3:     public Gender         Gender { get; set; }
   4:     public int            Age { get; set; }
   5:     public ContactInfo    ContactInfo { get; set; }
   6: }
   7:  
   8: public class ContactInfo
   9: {
  10:     public string EmailAddress { get; set; }
  11:     public string PhoneNo { get; set; }
  12: }
  13:  
  14: public enum Gender
  15: {
  16:     Male,
  17:     Female
  18: }

As a simple example, suppose need XML to represent a set of objects Profile (Profile with a defined type indicated above), we will naturally adopt the following configuration.

   1: <Profiles>
   2:   <Profile Gender="Male" Age="18">
   3:     <ContactInfo EmailAddress ="[email protected]" PhoneNo="123"/>
   4:   </Profile>
   5:   <Profile Gender="Male" Age="25">
   6:     <ContactInfo EmailAddress ="[email protected]" PhoneNo="456"/>
   7:   </Profile>
   8:   <Profile Gender="Male" Age="40">
   9:     <ContactInfo EmailAddress ="[email protected]" PhoneNo="789"/>
  10: </Profile>

For this XML structure, XmlConfigurationProvider will use "brute" manner as it is mapped "configuration tree" shown below. Since this tree directly to the XML element name as the configuration node name, so three Profile objects in the root of the tree are to "Profile" naming, no doubt, The tree will not be able to use a dictionary to represent, because it can not guarantee that all nodes have a different path .

image

Second, according to the requirements of the XML configuration tree structure conversion slightly

The reason why that can not be expressed as XML JSON format in a very natural form of collection or array, because the latter provides a clear definition of the way these two data types (using the definition in brackets), but only XML child elements concept, we can not determine whether it is a collection of child elements. If you make the assumption: If all sub-elements in the same XML elements have the same name, so we can be viewed as a collection . According to such a hypothesis, we XmlConfigurationSource slightly XML transformation can solve the problem difficult to represent a set of data structures.

We derive XmlConfigurationSource create a new type of ConfigurationSource, tentatively named it ExtendedXmlConfigurationSource. ConfigurationProvdier type XmlConfigurationSource provide for ExtendedXmlConfigurationProvider, which is derived from XmlConfigurationProvider. In the Load method overridden, ExtendedXmlConfigurationProvider by the original XML structure changes accordingly, thereby allowing the original legal XML (XML elements with the same name) can be converted into a dictionary configured for collection. The figure below shows the structure of XML rules and procedures employed in the conversion.

12

As shown above, the structure for converting the original set of XML made consists of two steps. The first step is a collection of elements to add XML elements named " append_index Properties" (Attribute), we have adopted a zero-based index as the value of the property. The second step will be to create a new XML based on the results of the first step in the conversion, the set of elements of the same name (such as <profile>) will be the new name (such as <index based on the value added profile_index_0 >). There is no doubt that transformed XML may well represent a collection of objects. ExtendedXmlConfigurationProvider is defined as shown below, the above-described conversion logic embodied in the Load method of rewriting.

   1: public class ExtendedXmlConfigurationProvider : XmlConfigurationProvider
   2: {
   3:    public ExtendedXmlConfigurationProvider(XmlConfigurationSource source) : base(source)
   4:     {}
   5:  
   6:     public override void Load(Stream stream)
   7:     {
   8: // load the source file and create a XmlDocument        
   9:         XmlDocument sourceDoc = new XmlDocument();
  10:         sourceDoc.Load(stream);
  11:  
  12: // add indexes
  13:         this.AddIndexes(sourceDoc.DocumentElement);
  14:  
  15: // Create a new XmlDocument according to the index added
  16:         XmlDocument newDoc = new XmlDocument();
  17:         XmlElement documentElement = newDoc.CreateElement(sourceDoc.DocumentElement.Name);
  18:         newDoc.AppendChild(documentElement);
  19:  
  20:         foreach (XmlElement element in sourceDoc.DocumentElement.ChildNodes)
  21:         {
  22:             this.Rebuild(element, documentElement, 
  23:                 name => newDoc.CreateElement(name));
  24:         }
  25:  
  26: // Under the new XmlDocument initial configuration dictionary
  27:         using (Stream newStream = new MemoryStream())
  28:         {
  29:             using (XmlWriter writer = XmlWriter.Create(newStream))
  30:             {
  31:                 newDoc.WriteTo(writer);
  32:             }
  33:             newStream.Position = 0;
  34:             base.Load(newStream);
  35:         }
  36:     }
  37:  
  38:     private void AddIndexes(XmlElement element)
  39:     {
  40:         if (element.ChildNodes.OfType<XmlElement>().Count() > 1)
  41:         {
  42:             if (element.ChildNodes.OfType<XmlElement>().GroupBy(it => it.Name).Count() == 1)
  43:             {
  44:                 int index = 0;
  45:                 foreach (XmlElement subElement in element.ChildNodes)
  46:                 {
  47:                     subElement.SetAttribute("append_index", (index++).ToString());
  48:                     AddIndexes(subElement);
  49:                 }
  50:             }
  51:         }
  52:     }
  53:  
  54:     private void Rebuild(XmlElement source, XmlElement destParent, Func<string, XmlElement> creator)
  55:     {
  56:         string index = source.GetAttribute("append_index");
  57:         string elementName = string.IsNullOrEmpty(index) ? source.Name : $"{source.Name}_index_{index}";
  58:         XmlElement element = creator(elementName);
  59:         destParent.AppendChild(element);
  60:         foreach (XmlAttribute attribute in source.Attributes)
  61:         {
  62:             if (attribute.Name != "append_index")
  63:             {
  64:                 element.SetAttribute(attribute.Name, attribute.Value);
  65:             }
  66:         }
  67:  
  68:         foreach (XmlElement subElement in source.ChildNodes)
  69:         {
  70:             Rebuild(subElement, element, creator);
  71:         }
  72:     }
  73: }

 

Physical file is the carrier of the original configuration of our most commonly used, the optimal configuration file format consists of three main, they are JSON, XML and INI, the corresponding configuration source type are JsonConfigurationSource, XmlConfigurationSource and IniConfigurationSource. But for .NET Core configuration system, we take for granted actually is not an ideal configuration XML source, and at least JSON comparison, it has an inherent disadvantage, and that is support for the collection of data structures is not satisfactory. [This article has been synchronized to the " ASP.NET Framework Core Secret " among]

First, why it is difficult to be represented by an elegant collection of XML for configuration

In the " Configuration Model Detailed Design ," a paper we design and implement configuration model are described in detail. In this article we say reflects the application configuration is a tree of hierarchy, as I call it "configuration tree", the specific configuration data by the configuration tree "leaf node" bearer. When the configuration data are converted from different sources after loading into a dictionary, I call it "Configuring the dictionary." In order to "Configure dictionary" able to store "configuration tree" of all the data and its structure, we need to store all leaf nodes in the configuration dictionary, path, and the value of the leaf node directly as Key and Value dictionary elements. Key is unique because the dictionary, which requires a configuration of each node in the tree must have a unique path. XmlConfigurationSource / XmlConfigurationProvider not well supported set of data structures problem arises here.

   1: public class Profile
   2: {
   3:     public Gender         Gender { get; set; }
   4:     public int            Age { get; set; }
   5:     public ContactInfo    ContactInfo { get; set; }
   6: }
   7:  
   8: public class ContactInfo
   9: {
  10:     public string EmailAddress { get; set; }
  11:     public string PhoneNo { get; set; }
  12: }
  13:  
  14: public enum Gender
  15: {
  16:     Male,
  17:     Female
  18: }

As a simple example, suppose need XML to represent a set of objects Profile (Profile with a defined type indicated above), we will naturally adopt the following configuration.

   1: <Profiles>
   2:   <Profile Gender="Male" Age="18">
   3:     <ContactInfo EmailAddress ="[email protected]" PhoneNo="123"/>
   4:   </Profile>
   5:   <Profile Gender="Male" Age="25">
   6:     <ContactInfo EmailAddress ="[email protected]" PhoneNo="456"/>
   7:   </Profile>
   8:   <Profile Gender="Male" Age="40">
   9:     <ContactInfo EmailAddress ="[email protected]" PhoneNo="789"/>
  10: </Profile>

For this XML structure, XmlConfigurationProvider will use "brute" manner as it is mapped "configuration tree" shown below. Since this tree directly to the XML element name as the configuration node name, so three Profile objects in the root of the tree are to "Profile" naming, no doubt, The tree will not be able to use a dictionary to represent, because it can not guarantee that all nodes have a different path .

image

Second, according to the requirements of the XML configuration tree structure conversion slightly

The reason why that can not be expressed as XML JSON format in a very natural form of collection or array, because the latter provides a clear definition of the way these two data types (using the definition in brackets), but only XML child elements concept, we can not determine whether it is a collection of child elements. If you make the assumption: If all sub-elements in the same XML elements have the same name, so we can be viewed as a collection . According to such a hypothesis, we XmlConfigurationSource slightly XML transformation can solve the problem difficult to represent a set of data structures.

We derive XmlConfigurationSource create a new type of ConfigurationSource, tentatively named it ExtendedXmlConfigurationSource. ConfigurationProvdier type XmlConfigurationSource provide for ExtendedXmlConfigurationProvider, which is derived from XmlConfigurationProvider. In the Load method overridden, ExtendedXmlConfigurationProvider by the original XML structure changes accordingly, thereby allowing the original legal XML (XML elements with the same name) can be converted into a dictionary configured for collection. The figure below shows the structure of XML rules and procedures employed in the conversion.

12

As shown above, the structure for converting the original set of XML made consists of two steps. The first step is a collection of elements to add XML elements named " append_index Properties" (Attribute), we have adopted a zero-based index as the value of the property. The second step will be to create a new XML based on the results of the first step in the conversion, the set of elements of the same name (such as <profile>) will be the new name (such as <index based on the value added profile_index_0 >). There is no doubt that transformed XML may well represent a collection of objects. ExtendedXmlConfigurationProvider is defined as shown below, the above-described conversion logic embodied in the Load method of rewriting.

   1: public class ExtendedXmlConfigurationProvider : XmlConfigurationProvider
   2: {
   3:    public ExtendedXmlConfigurationProvider(XmlConfigurationSource source) : base(source)
   4:     {}
   5:  
   6:     public override void Load(Stream stream)
   7:     {
   8: // load the source file and create a XmlDocument        
   9:         XmlDocument sourceDoc = new XmlDocument();
  10:         sourceDoc.Load(stream);
  11:  
  12: // add indexes
  13:         this.AddIndexes(sourceDoc.DocumentElement);
  14:  
  15: // Create a new XmlDocument according to the index added
  16:         XmlDocument newDoc = new XmlDocument();
  17:         XmlElement documentElement = newDoc.CreateElement(sourceDoc.DocumentElement.Name);
  18:         newDoc.AppendChild(documentElement);
  19:  
  20:         foreach (XmlElement element in sourceDoc.DocumentElement.ChildNodes)
  21:         {
  22:             this.Rebuild(element, documentElement, 
  23:                 name => newDoc.CreateElement(name));
  24:         }
  25:  
  26: // Under the new XmlDocument initial configuration dictionary
  27:         using (Stream newStream = new MemoryStream())
  28:         {
  29:             using (XmlWriter writer = XmlWriter.Create(newStream))
  30:             {
  31:                 newDoc.WriteTo(writer);
  32:             }
  33:             newStream.Position = 0;
  34:             base.Load(newStream);
  35:         }
  36:     }
  37:  
  38:     private void AddIndexes(XmlElement element)
  39:     {
  40:         if (element.ChildNodes.OfType<XmlElement>().Count() > 1)
  41:         {
  42:             if (element.ChildNodes.OfType<XmlElement>().GroupBy(it => it.Name).Count() == 1)
  43:             {
  44:                 int index = 0;
  45:                 foreach (XmlElement subElement in element.ChildNodes)
  46:                 {
  47:                     subElement.SetAttribute("append_index", (index++).ToString());
  48:                     AddIndexes(subElement);
  49:                 }
  50:             }
  51:         }
  52:     }
  53:  
  54:     private void Rebuild(XmlElement source, XmlElement destParent, Func<string, XmlElement> creator)
  55:     {
  56:         string index = source.GetAttribute("append_index");
  57:         string elementName = string.IsNullOrEmpty(index) ? source.Name : $"{source.Name}_index_{index}";
  58:         XmlElement element = creator(elementName);
  59:         destParent.AppendChild(element);
  60:         foreach (XmlAttribute attribute in source.Attributes)
  61:         {
  62:             if (attribute.Name != "append_index")
  63:             {
  64:                 element.SetAttribute(attribute.Name, attribute.Value);
  65:             }
  66:         }
  67:  
  68:         foreach (XmlElement subElement in source.ChildNodes)
  69:         {
  70:             Rebuild(subElement, element, creator);
  71:         }
  72:     }
  73: }

 

Guess you like

Origin www.cnblogs.com/webenh/p/11589972.html