电子商务--配置管理

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

      好了,我先介绍下.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类,自定义节点类,和设计一个配置管理类,来配置我们的各种信息。各有优缺点。大家在实际的项目开发中,根据网站配置信息多少,选择适合自己的配置方式。

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

posted @ 2013-06-05 10:13  喜洋羊  阅读(640)  评论(1编辑  收藏  举报