zoukankan      html  css  js  c++  java
  • 电子商务配置管理

       在大型项目开发中,我们需要配置各种各样的信息。比如:在线支付方式有支付宝,网银,财付通,快钱。我们需要配置支付宝账号信息,需要配置财付通账号信息,需要配置网银账号信息还有快钱账号信息。发邮件呢?我们需要配置邮件服务器信息。发短信呢?我们需要配置短息服务器信息。对于各种各样形形色色的配置信息,我们该如何配置呢?又该如何才能方便管理呢?我们当然希望有一个通用的配置管理模块来管理我们的配置信息,方便我们定位配置信息,方便修改,而且修改配置信息不会引起站点的重启等等。

          好了,我先介绍下.Net自带的配置管理,然后在使用反射技术定义一个通用配置模块。

      1)使用System.Configuration.AppSettingsSection类 

            .Net已经提供了AppSettingsSection类方便我们配置信息。我们可以把配置信息放在Web.confg或App.config的appSettings节点下。如下:

        <configuration>
            <appSettings>
                <!--支付宝合作身份者Id-->
                <add key="AlipayId" value="1234567890"/>
                <add key="AlipayKey" value="test1234567890"/>
                <add key="AlipayEmail" value="yangyanping0615@163.com"/>

                <!--财付通商户号-->
                <add key="TenpayId" value="1234567890"></add>
                <!--财付通密钥-->
                <add key="TenpayKey" value="test1234567890"/>

                <!--网银商户号-->
                <add key="ChinaBankId" value="1001"></add>
                <!--网银密钥-->
                <add key="ChinaBankKey" value="test"/>
            </appSettings>
        </configuration>

      这种配置方式是.Net最基本的配置方式,也是我们最常用的配置方式,但这样的配置方式显然不能让我们满意。因为大量的配置信息放在一起,即无法对信息进行分组,也不方便管理,时间一长,我们自己可能已经不记得配置信息的意思,而且修改任何的配置内容都会导致Asp.Net站点重新启动。

          2)使用System.Configuration.NameValueSectionHandler类 

             .Net在System下提供NameValueSectionHandler类可以对配置信息进行方便的分组。NameValueSectionHandler类的定义是这样的:

     public class NameValueSectionHandler : IConfigurationSectionHandler
        {
            // Fields
            private const string defaultKeyAttribute = "key";
            private const string defaultValueAttribute = "value";
    
            // Methods
            [TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")]
            public NameValueSectionHandler();
            public object Create(object parent, object context, XmlNode section);
            internal static object CreateStatic(object parent, XmlNode section);
            internal static object CreateStatic(object parent, XmlNode section, string keyAttriuteName, string valueAttributeName);
    
            // Properties
            protected virtual string KeyAttributeName { get; }
            protected virtual string ValueAttributeName { get; }
        }
    View Code

        NameValueSectionHandler类实现了接口IConfigurationSectionHandler的Create方法,返回一个ReadOnlyNameValueCollection

    internal static object CreateStatic(object parent, XmlNode section, string keyAttriuteName, string valueAttributeName)
        {
            ReadOnlyNameValueCollection values;
            if (parent == null)
            {
                values = new ReadOnlyNameValueCollection(StringComparer.OrdinalIgnoreCase);
            }
            else
            {
                ReadOnlyNameValueCollection values2 = (ReadOnlyNameValueCollection) parent;
                values = new ReadOnlyNameValueCollection(values2);
            }
            HandlerBase.CheckForUnrecognizedAttributes(section);
            foreach (XmlNode node in section.ChildNodes)
            {
                if (!HandlerBase.IsIgnorableAlsoCheckForNonElement(node))
                {
                    if (node.Name == "add")
                    {
                        string str = HandlerBase.RemoveRequiredAttribute(node, keyAttriuteName);
                        string str2 = HandlerBase.RemoveRequiredAttribute(node, valueAttributeName, true);
                        HandlerBase.CheckForUnrecognizedAttributes(node);
                        values[str] = str2;
                    }
                  else
                  {
                       if (node.Name == "remove")
                        {
                            string name = HandlerBase.RemoveRequiredAttribute(node, keyAttriuteName);
                            HandlerBase.CheckForUnrecognizedAttributes(node);
                            values.Remove(name);
                            continue;
                        }
                      if (node.Name.Equals("clear"))
                        {
                            HandlerBase.CheckForUnrecognizedAttributes(node);
                            values.Clear();
                            continue;
                        }
                       HandlerBase.ThrowUnrecognizedElement(node);
                  }
              }
          }
          values.SetReadOnly();
          return values;
      }
    View Code

     从代码中,可以看出需要一个Add节点,该节点包含一个Key和Value的属性。我们可以这样配置各种信息。 

     <configuration>
            <configSections>
                <section name="Alipay" type="System.Configuration.NameValueSectionHandler"/>
                <section name="TenPay" type="System.Configuration.NameValueSectionHandler"/>
                <section name="WebBank" type="System.Configuration.NameValueSectionHandler"/>
          </configSections>
            <Alipay>
                <!--合作身份者ID-->
                <add key="Id"  value="1234567890" />
                <!--安全检验码-->
                <add key="key"  value="test1234567890" />
                <add key="email"  value="alipay-test03@alipay.com" />
            </Alipay>
          <TenPay>
                <!--合作身份者ID-->
                <add key="Id"  value="1234567890" />
                <!--安全检验码-->
                <add key="key"  value="test1234567890" />
          </TenPay>
          <WebBank>
                <!--合作身份者ID-->
                <add key="Id"  value="1001" />
                <!--安全检验码-->
                <add key="key"  value="test1234567890" />
              </WebBank>
      </configuration>  
    View Code

      可以这样读取配置信息:

       //支付宝配置信息
          var alipays = (NameValueCollection)ConfigurationManager.GetSection("Alipay");
          //支付宝Id
          string alipayId = alipays["Id"];
          //支付宝Key
          string alipaykey = alipays["key"];

      这种配置方式可以有效的对各种不同的配置信息进行分组,方便查看和修改。但不符合面向对象的开发,也无法对配置的信息进行类型检查,修改配置信息一样会导致Asp.Net站点重新启动。

      3)自定义配置节点

         我们可以仿照AppSettingsSection类的实现方式,自定义节点配置类。可以继承ConfigurationSection类实现自定义节点配置类。

       

    public class AlipaySetion : ConfigurationSection
        {
            [ConfigurationProperty("Id", IsKey = true)]
            public string Id
            {
                get { return (string)base["Id"]; }
                set { base["Id"] = value; }
            }
    
            [ConfigurationProperty("Key")]
            public string Key
            {
                get { return (string)base["Key"]; }
                set { base["Key"] = value; }
            }
    
            [ConfigurationProperty("Email")]
            public string Email
            {
                get { return (string)base["Email"]; }
                set { base["Email"] = value; }
            }
        }
    View Code

      节点配置如下:

      <configuration>
          <configSections>
              <section name="Alipay" type="ConsoleApplication2.AlipaySetion,ConsoleApplication2"/>
          </configSections>
          <Alipay Id="1234567890" Key="test1234567890" Email="alipay-test03@alipay.com"/>
      </configuration>

      读取方式:

          var alipays = (AlipaySetion)ConfigurationManager.GetSection("Alipay");
                  string id = alipays.Id;
                  string key = alipays.Key;

      自定义配置节点方便查看和管理,也符合面向对象开发和进行类型检查。但我们需要写很多ConfigurationSection类的实现,显然无法满足我们的需求。

         4)通用的配置模块

      .Net中使用反射技术可以动态的创建一个对象,可以调用对象的方法,可以给对象属性赋值。我们是不是可以根据这个特点动态的创建配置类,然后给配置类的属性赋值呢?那需要什么呢?需要2个基础文件,一个是对象的映射关系文件,另一个是对象属性映射文件,如图:

      首先来看看对象的映射关系文件(文件名:System.config),内容如下:

     

    <configuration>
      <configMappings>
        <mapping  AssemblyName="ConsoleApplication2" ClassName="ConsoleApplication2.AlipayConfiguration"
                 Name="AlipayConfiguration"
                 section="AlipayConfiguration"
                 fileName="Mapping.AlipayConfiguration.config">
            <field xmlName="Id" fieldName="Id" />
            <field xmlName="Key" fieldName="Key" />
            <field xmlName="Email" fieldName="Email" />
        </mapping>
        <mapping  AssemblyName="ConsoleApplication2" ClassName="ConsoleApplication2.TenpayConfiguration"
          Name="TenpayConfiguration"
                section="TenpayConfiguration"
                fileName="Mapping.TenpayConfiguration.config">
          <field xmlName="Id" fieldName="Id" />
          <field xmlName="Key" fieldName="Key" />
        </mapping>
      </configMappings>
    </configuration>
    View Code

       对象属性映射文件:

      支付宝配置类文件(Mapping.AlipayConfiguration.config)

      <?xml version="1.0" encoding="utf-8" ?>
        <AlipayConfiguration>
            <node name="Id" value="1234567890"/>
            <node name="Key" value="test1234567890"/>
            <node name="Email" value="alipay-test03@alipay.com" />
        </AlipayConfiguration>

      Tenpay配置类文件(Mapping.TenpayConfiguration.config)

      <?xml version="1.0" encoding="utf-8" ?>
        <TenpayConfiguration>
            <node name="Id" value="1234567890"/>
            <node name="Key" value="test1234567890"/>
        </TenpayConfiguration>

         xml文件我就不做过多说明了,很简单。看到节点名称基本就能猜出其意思。下面来设计映射类和配置管理类,配置管理类顾名思义就能知道是管理所有配置类的。那怎样才能管理所有的配置类呢?我们知道object是所有对象的基类。当然我们的配置类也是继承object的。我们可以设计一个键值对的集合来存放所有的配置信息。比如:private Dictionary<string, object> Mappings。其中的keyi就是配置的类名,value就是配置对象。我们先看看对象的映射类的定义:

       

    public class ConfigMapping
        {
            private Dictionary<string, string> fieldMappings;
    
            /// <summary>
            /// 配置名(唯一)
            /// </summary>
            public string Name
            {
                get;
                set;
            }
    
            /// <summary>
            /// 配置对象程序集名
            /// </summary>
            public string AssemblyName
            {
                get;
                set;
            }
    
            /// <summary>
            /// 配置对象类名
            /// </summary>
            public string ClassName
            {
                get;
                set;
            }
    
            /// <summary>
            /// 配置对象文件
            /// </summary>
            public string ConfigFile
            {
                get;
                set;
            }
    
            /// <summary>
            /// 类型
            /// </summary>
            private Type type;
    
            /// <summary>
            /// 配置对象类型
            /// </summary>
            public Type Type
            {
                get
                {
                    return Type.GetType(ClassName + "," + AssemblyName);
                }
                set
                {
                    type = value;
                    Instance = Activator.CreateInstance(Type.GetType(ClassName + "," + AssemblyName));
                }
            }
    
            /// <summary>
            /// 配置对象实例
            /// </summary>
            public object Instance
            {
                get;
                private set;
            }
    
            //域集合
            public Dictionary<string, string> FieldMappings
            {
                get
                {
                    return fieldMappings ?? (new Dictionary<string, string>());
                }
                set
                {
                    fieldMappings = value;
                }
            }
        }
    View Code

      配置管理类ConfigManager代码:

        

     public class ConfigManager
        {
            private static readonly object sync = new object();
            private static ConfigManager manager;
            private Dictionary<string, object> Mappings;
            private const string configFile = "Configuration/System.config";
    
            private ConfigManager()
            {
                Mappings = new Dictionary<string, object>();
            }
    
            public static ConfigManager Instance
            {
                get
                {
                    lock (sync)
                    {
                        if (manager == null)
                        {
                            lock (sync)
                            {
                                manager = new ConfigManager();
                                manager.Init();
                            }
                        }
                    }
    
                    return manager;
                }
            }
    
            /// <summary>
            /// 初始化配置管理
            /// </summary>
            privatevoid Init()
            {
                string path = AppDomain.CurrentDomain.BaseDirectory + configFile;
    
                var document = new XmlDocument();
                document.Load(path);
    
                var nodes = document.SelectNodes("configuration/configMappings/mapping");
    
                foreach (XmlNode node in nodes)
                {
                    var mapping = new ConfigMapping
                                      {
                                          Name = node.Attributes["Name"].InnerText,
                                          AssemblyName = node.Attributes["AssemblyName"].InnerText,
                                          ClassName = node.Attributes["ClassName"].InnerText,
                                          ConfigFile = node.Attributes["fileName"].InnerText,
                                          Type =
                                              Type.GetType(node.Attributes["ClassName"].InnerText + "," +
                                                           node.Attributes["AssemblyName"].InnerText)
                                      };
    
                    foreach (XmlNode childNode in node.SelectNodes("field"))
                    {
                        mapping.FieldMappings.Add(childNode.Attributes["xmlName"].InnerText,
                                                  childNode.Attributes["fieldName"].InnerText);
                    }
    
                    SetProperty(mapping);
    
                    Mappings.Add(mapping.Name, mapping.Instance);
                }
            }
    
            /// <summary>
            /// 设置对象属性值
            /// </summary>
            /// <param name="mapping"></param>
            private void SetProperty(ConfigMapping mapping)
            {
                var xml = new XmlDocument();
                xml.Load(AppDomain.CurrentDomain.BaseDirectory + "Configuration/" + mapping.ConfigFile);
                var ns = xml.SelectSingleNode(mapping.Name);
                var infos = mapping.Instance.GetType().GetProperties();
    
                foreach (var info in infos)
                {
                    foreach (XmlNode n in ns.ChildNodes)
                    {
                        if (n.Attributes["name"].Value == info.Name)
                        {
                            object value = Convert.ChangeType(n.Attributes["value"].Value, info.PropertyType);
                            info.SetValue(mapping.Instance, value, null);
                        }
                    }
                }
            }
    
            /// <summary>
            /// 获取配置类实例
            /// </summary>
            /// <param name="name"></param>
            /// <returns></returns>
            public object GetMappingByName(string name)
            {
                return Mappings[name];
            }
        }
    View Code

      读取配置信息很简单,代码如下:

       var alipay = (AlipayConfiguration) ConfigManager.Instance.GetMappingByName("AlipayConfiguration");
                long alipayId = alipay.Id;
                string alipayKey = alipay.Key;

                var tenpay = (TenpayConfiguration)ConfigManager.Instance.GetMappingByName("TenpayConfiguration");
                long tenpayId = tenpay.Id;
                string tenpayKey = tenpay.Key;

       总结:这篇文章主要是介绍了AppSettingsSection类,NameValueSectionHandler类,自定义节点类,和设计一个配置管理类,来配置我们的各种信息。各有优缺点。大家在实际的项目开发中,根据网站配置信息多少,选择适合自己的配置方式。

      好了,感谢阅读,希望这篇文章能给你带来帮助!

  • 相关阅读:
    【贪心】【堆】Gym
    【并查集】Gym
    【拓扑排序】【bitset】Gym
    【递归】【线段树】【堆】AtCoder Regular Contest 080 E
    【二分图】【并查集】XVII Open Cup named after E.V. Pankratiev Stage 14, Grand Prix of Tatarstan, Sunday, April 2, 2017 Problem L. Canonical duel
    【动态规划】【滚动数组】【bitset】XVII Open Cup named after E.V. Pankratiev Stage 14, Grand Prix of Tatarstan, Sunday, April 2, 2017 Problem J. Terminal
    【二分】【字符串哈希】【二分图最大匹配】【最大流】XVII Open Cup named after E.V. Pankratiev Stage 14, Grand Prix of Tatarstan, Sunday, April 2, 2017 Problem I. Minimum Prefix
    【枚举】【最小表示法】XVII Open Cup named after E.V. Pankratiev Stage 14, Grand Prix of Tatarstan, Sunday, April 2, 2017 Problem F. Matrix Game
    【推导】【构造】XVII Open Cup named after E.V. Pankratiev Stage 14, Grand Prix of Tatarstan, Sunday, April 2, 2017 Problem E. Space Tourists
    【推导】【贪心】XVII Open Cup named after E.V. Pankratiev Stage 14, Grand Prix of Tatarstan, Sunday, April 2, 2017 Problem D. Clones and Treasures
  • 原文地址:https://www.cnblogs.com/xiyangyang/p/3099114.html
Copyright © 2011-2022 走看看