好得很程序员自学网

<tfoot draggable='sEl'></tfoot>

项目中配置文件优化方案

项目中配置文件优化方案

项目中配置文件优化方案

最近在优化项目中的配置文件,苦思冥想了n种解决方案,就连晚上睡觉脑子里也是Config配置节点。好吧,也许有的人会疑问,为什么要优化或者说整合项目中的配置文件呢?说白了,也是项目前期没有对配置文件有个很好的总体考虑,当项目越来越大,与其他系统之间又紧密联系在一起,你会发现项目中无论是自身的配置还是第三方的配置都很多,如果之前没有一个很好的规划,配置节点会放的到处都是,而且是毫无章法,根本区分不出那一个配置节点是哪一个模块的,这样就显得很凌乱。处于这样一个背景下,所以我们要优化配置文件,使其分块放置,看起来一目了然。于是乎我又不知道死了多少脑细胞,好吧,谁让咱年轻呢,有的就是脑细胞,早上两个鸡蛋饼后开始了我迷茫的思考,正想的起劲呢,旁边一破孩问我中午吃什么,我才意识到该午餐了(一到午餐时间大家都在纠结午餐吃什么),算了,不想了,将砂锅进行到底吧,两大荤把脑细胞补回来。饭毕,继续沉浸在一个人的程序世界。如此过了几天,也尝试做了一个优化方案,灵感来源于log4net(一个开源日志框架)对配置的实现方式,其间也研究了一下log4net的开源代码,收获颇多。

    废话不多说了,进入正题,前面说过我们是配置的优化,主要实现以下功能: 

   1.配置节点的整合,使配置项分模块放置,达到清晰明了的目的,例如系统配置、工作流配置、第三方配置等等。

   2.使开发人员使用配置项简单易用。

   3.在配置文件中的属性值一旦被修改,则不需要重新启动服务去加载配置文件,程序会自动加载配置文件。

   我优化后的部分配置项如下,一个配置文件中只有一个<configs></configs>节点,其中可以包括多个<config></config>节点,每一个<config></config>节点会有一个相对应的实体类与之对应,也就是说一个<config></config>节点就表示一个模块的配置,节点中的name命名为类名、type是类的类型。而<config></config>节点下的众多<property />节点则是当前模块的配置项也是实体类的属性。如下所示IsTestMode就是系统配置模块的一个配置项,这样就很清晰的把配置项分模块管理。

View Code

 <?  xml version="1.0" encoding="utf-8"   ?> 
 <  configs  > 
   <  config   name  ="SystemConfig"   description  ="系统配置"   type  ="ConfigurationCollection.ServerConfig.SystemConfig, ConfigurationCollection, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"  > 
     <  property   key  ="IsTestMode"   value  ="true"  /> 
     <  property   key  ="TestModeZoneNo"   value  ="93"  /> 
     <  property   key  ="TestModeOrgId"   value  ="9301"  /> 
     <  property   key  ="IsVerifyMac"   value  ="false"  /> 
     <  property   key  ="SessionRecorderInDebugMode"   value  ="true"  /> 
     <  property   key  ="ServerName"   value  ="AstCoreService1"  /> 
     <  property   key  ="HistoryDbName"   value  ="FES_AST_H"  /> 
     <  property   key  ="ExternalDbName"   value  ="IMPORTDATA"  /> 
   </  config  > 
  
   <  config   name  ="IPPConfig"   description  ="IPP配置"   type  ="ConfigurationCollection.ServerConfig.IPPConfig, ConfigurationCollection, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"  > 
     <  property   key  ="RecvMsgUseTestCase"   value  ="false"  /> 
     <  property   key  ="LocalIPV4Scope"   value  ="10."  /> 
     <  property   key  ="MainFrameTargetAddress"   value  ="10.112.18.58"  /> 
     <  property   key  ="TcpCommTimeout"   value  ="60000"  /> 
     <  MainFrameService   key  ="MainFrameService"   dslVersion  ="1.0.0.0"   Id  ="c9c64477-70fd-4089-a988-b63600fe663e"   xmlns  ="http://schemas.microsoft.com/dsltools/TradeDesigner"  > 
       <  Service   name  ="SPD_IPPSRV0"   channel  ="SPDTCP"   server  ="10.112.18.58:6005"   servicetype  =""  /> 
       <  Service   name  ="SPD_IPPSRV1"   channel  ="SPDTCP"   server  ="10.112.18.58:6005"   servicetype  =""  /> 
       <  Service   name  ="SPD_IPPSRV3"   channel  ="SPDTCP"   server  ="10.112.5.78:8991"   servicetype  =""  /> 
     </  MainFrameService  > 
   </  config  > 
 </  configs  > 

   上面介绍了优化后的配置文件结构,我们又如何加载到我们的应用当中去呢,下面是加载配置文件组件的结构,Config中是加载配置文件的入口、Util中是具体实现如何加载配置文件到具体的实体类、ClientConfig和ServrConfig是分别放客户端和服务端配置文件对应的实体类。

    实体类的创建,上面说过一个配置模块对应一个实体类,而<property />节点则是当前模块的配置项也就是实体类的属性,这个我定义了静态属性,并且把set设置为private,就是不能让开发人员去修改公共的配置项。如下所示定义了配置文件中第一个<config></config>节点,也就是系统配置的实体类:

View Code

 public   class   SystemConfig
    {
          public   static   bool  IsTestMode {  get ;  private   set  ; }

          public   static   int  TestModeZoneNo {  get ;  private   set  ; }

          public   static   string  TestModeOrgId {  get ;  private   set  ; }

          public   static   string  IsVerifyMac {  get ;  private   set  ; }

          public   static   string  SessionRecorderInDebugMode {  get ;  private   set  ; }

          public   static   string  ServerName {  get ;  private   set  ; }

          public   static   string  HistoryDbName {  get ;  private   set  ; }

          public   static   string  ExternalDbName {  get ;  private   set  ; }
    } 

     一切就绪只欠东风,下面说说配置文件是怎样加载到实体类中的,首先引用上面的项目或者生成的dll,再利用Config中入口方法加载配置文件,如下,这里ServerConfig.config是配置文件的名称,它放在程序运行的根目录下的Configs文件夹下:

View Code

 private   void  SetParameter(XmlElement element,  object   target)
        {
              string  key =  element.GetAttribute(KEY_ATTR);

              if  (key ==  null  || key.Length ==  0  )
            {
                key  =  element.LocalName;
            }

            Type targetType  =  target.GetType();
            Type propertyType  =  null  ;

            PropertyInfo propInfo  =  null  ;
            MethodInfo methInfo  =  null  ;

            propInfo  = targetType.GetProperty(key, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.IgnoreCase |  BindingFlags.Static);
              if  (propInfo !=  null  &&  propInfo.CanWrite)
            {
                propertyType  =  propInfo.PropertyType;
            }
              else  
            {
                propInfo  =  null  ;

                methInfo  =  FindMethodInfo(targetType, key);

                  if  (methInfo !=  null  )
                {
                    propertyType  = methInfo.GetParameters()[ 0  ].ParameterType;
                }
            }

              if  (propertyType ==  null  )
            {
                  //  LogLog.Error(declaringType, "XmlHierarchyConfigurator: Cannot find Property [" + name + "] to set object on [" + target.ToString() + "]"); 
             }
              else  
            {
                  string  propertyValue =  null  ;

                  if  (element.GetAttributeNode(VALUE_ATTR) !=  null  )
                {
                    propertyValue  =  element.GetAttribute(VALUE_ATTR);
                }
                  else   if   (element.HasChildNodes)
                {
                      //   属性下面还有Node 
                     foreach  (XmlNode childNode  in   element.ChildNodes)
                    {
                          if  (childNode.NodeType == XmlNodeType.CDATA || childNode.NodeType ==  XmlNodeType.Text)
                        {
                              if  (propertyValue ==  null  )
                            {
                                propertyValue  =  childNode.InnerText;
                            }
                              else  
                            {
                                propertyValue  +=  childNode.InnerText;
                            }
                        }
                          else  
                        {
                            propertyValue  =  element.OuterXml;
                        }
                    }
                }

                  if  (propertyValue !=  null  )
                {
                      try  
                    {
                        propertyValue  =  OptionConverter.SubstituteVariables(propertyValue, Environment.GetEnvironmentVariables());
                    }
                      catch   (System.Security.SecurityException)
                    {
                          //  LogLog.Debug(declaringType, "Security exception while trying to expand environment variables. Error Ignored. No Expansion."); 
                     }

                    Type parsedObjectConversionTargetType  =  null  ;

                      string  subTypeString =  element.GetAttribute(TYPE_ATTR);
                      if  (subTypeString !=  null  && subTypeString.Length >  0  )
                    {
                          try  
                        {
                            Type subType  = SystemInfo.GetTypeFromString(subTypeString,  true ,  true  );

                              if  (! propertyType.IsAssignableFrom(subType))
                            {
                                  if   (OptionConverter.CanConvertTypeTo(subType, propertyType))
                                {
                                    parsedObjectConversionTargetType  =  propertyType;

                                    propertyType  =  subType;
                                }
                                  else  
                                {
                                      //  LogLog.Error(declaringType, "subtype [" + subType.FullName + "] set on [" + name + "] is not a subclass of property type [" + propertyType.FullName + "] and there are no acceptable type conversions."); 
                                 }
                            }
                              else  
                            {
                                propertyType  =  subType;
                            }
                        }
                          catch   (Exception ex)
                        {
                              //  LogLog.Error(declaringType, "Failed to find type [" + subTypeString + "] set on [" + name + "]", ex); 
                         }
                    }

                      object  convertedValue =  OptionConverter.ConvertStringTo(propertyType, propertyValue);

                      if  (convertedValue !=  null  && parsedObjectConversionTargetType !=  null  )
                    {
                        convertedValue  =  OptionConverter.ConvertTypeTo(convertedValue, parsedObjectConversionTargetType);
                    }

                      if  (convertedValue !=  null  )
                    {
                          if  (propInfo !=  null  )
                        {
                              try  
                            {
                                propInfo.SetValue(target, convertedValue, BindingFlags.SetProperty,   null ,  null  , CultureInfo.InvariantCulture);
                            }
                              catch   (TargetInvocationException targetInvocationEx)
                            {
                                  //  LogLog.Error(declaringType, "Failed to set parameter [" + propInfo.Name + "] on object [" + target + "] using value [" + convertedValue + "]", targetInvocationEx.InnerException); 
                             }
                        }
                          else   if  (methInfo !=  null  )
                        {
                              try  
                            {
                                methInfo.Invoke(target, BindingFlags.InvokeMethod,   null ,  new   object  [] { convertedValue }, CultureInfo.InvariantCulture);
                            }
                              catch   (TargetInvocationException targetInvocationEx)
                            {
                                  //  LogLog.Error(declaringType, "Failed to set parameter [" + name + "] on object [" + target + "] using value [" + convertedValue + "]", targetInvocationEx.InnerException); 
                             }
                        }
                    }
                      else  
                    {
                          //  LogLog.Warn(declaringType, "Unable to set property [" + name + "] on object [" + target + "] using value [" + propertyValue + "] (with acceptable conversion types)"); 
                     }
                }
                  else  
                {
                      object  createdObject =  null  ;

                      if  (propertyType ==  typeof ( string ) && ! HasAttributesOrElements(element))
                    {
                        createdObject  =  ""  ;
                    }
                      else  
                    {
                        Type defaultObjectType  =  null  ;
                          if   (IsTypeConstructible(propertyType))
                        {
                            defaultObjectType  =  propertyType;
                        }

                        createdObject  =  CreateObjectFromXml(element, defaultObjectType, propertyType);
                    }

                      if  (createdObject ==  null  )
                    {
                          //  LogLog.Error(declaringType, "Failed to create object to set param: " + name); 
                     }
                      else  
                    {
                          if  (propInfo !=  null  )
                        {
                              try  
                            {
                                propInfo.SetValue(target, createdObject, BindingFlags.SetProperty,   null ,  null  , CultureInfo.InvariantCulture);
                            }
                              catch   (TargetInvocationException targetInvocationEx)
                            {
                                  //  LogLog.Error(declaringType, "Failed to set parameter [" + propInfo.Name + "] on object [" + target + "] using value [" + createdObject + "]", targetInvocationEx.InnerException); 
                             }
                        }
                          else   if  (methInfo !=  null  )
                        {
                              try  
                            {
                                methInfo.Invoke(target, BindingFlags.InvokeMethod,   null ,  new   object  [] { createdObject }, CultureInfo.InvariantCulture);
                            }
                              catch   (TargetInvocationException targetInvocationEx)
                            {
                                  //  LogLog.Error(declaringType, "Failed to set parameter [" + methInfo.Name + "] on object [" + target + "] using value [" + createdObject + "]", targetInvocationEx.InnerException); 
                             }
                        }
                    }
                }
            }
        } 
ConfigurationCollection.Config.XmlConfigurator.Configure( new  System.IO.FileInfo( "  .\\Configs\\ServerConfig.config  " ));

    读取配置文件只是第一步,后面将讲述如何把配置文件转化成对应的实体类。首先读取配置文件后,我会去解析XML,把一个<configs></configs>节点下的多个<config></config>模块分组处理,就拿系统配置(SystemConfig)举例,当解析到SystemConfig模块时,我会去拿<config></config>中的type,然后创建对应的实例,同时分别解析<config></config>节点下的<property />节点,<property />节点的key就是属性名,因此就可以拿上面创建的实例去反射查找当前属性,并得到实体类中当前属性的类型,,<property />节点中的value值即是属性的值,根据反射得到的属性类型,再把value值转换成对应的类型即可(这里的转化可以自定义,自定代码在Util中实现,也可以用.net自带的一些方法转化,例如Parse)。说白了,就是利用.net的反射机制实现。代码片段如下:

View Code

 <  MainFrameService   key  ="MainFrameService"   dslVersion  ="1.0.0.0"   Id  ="c9c64477-70fd-4089-a988-b63600fe663e"   xmlns  ="http://schemas.microsoft.com/dsltools/TradeDesigner"  > 
       <  Service   name  ="SPD_IPPSRV0"   channel  ="SPDTCP"   server  ="10.112.18.58:6005"   servicetype  =""  /> 
       <  Service   name  ="SPD_IPPSRV1"   channel  ="SPDTCP"   server  ="10.112.18.58:6005"   servicetype  =""  /> 
       <  Service   name  ="SPD_IPPSRV3"   channel  ="SPDTCP"   server  ="10.112.5.78:8991"   servicetype  =""  /> 
     </  MainFrameService  > 

   对于一些不规则的配置项,就直接用XML格式作为value的值,在程序中再获取具体的属性值。如下这种配置项:

View Code

txtIsTestMode.Text = SystemConfig.IsTestMode.ToString();

   接下来该如何使用呢,其实很简单了,直接上料,如下是获取系统配置中的IsTestMode配置项,是不是很简单呢:

View Code

   前面已经实现的配置文件的优化,但有些时候,我为了改一下配置项的属性值还要重新启动服务,尤其是多台服务器,相当麻烦,这就需要监听配置文件有没有被修改,如果修改了则重新加载配置文件,就会用到.net中的FileSystemWatcher类。代码如下实现监听配置文件:

View Code

 public   MainWindow()
        {
            InitializeComponent();

              //   监听配置文件 
             ConfigFileWatcher();

              //   设置配置文件 
             SetConfigEnvironment();
        }

  ///   <summary> 
         ///   监听配置文件
          ///   </summary> 
         public   void   ConfigFileWatcher()
        {
            FileSystemWatcher watcher  =  new   FileSystemWatcher();
            watcher.Path  = AppDomain.CurrentDomain.BaseDirectory +  "  Configs\\  "  ;
            watcher.NotifyFilter  = NotifyFilters.LastAccess |  NotifyFilters.LastWrite;
              //   只监控.config文件   
            watcher.Filter =  "  *.config  "  ;
              //   添加事件处理器。   
            watcher.Changed +=  new   FileSystemEventHandler(OnChanged);
              //   开始监控。   
            watcher.EnableRaisingEvents =  true  ;
        }

          public   void  OnChanged( object   source, FileSystemEventArgs e)
        {
            SetConfigEnvironment();
        } 
          public   void   SetConfigEnvironment()
        {
              //   初始化配置文件 
            ConfigurationCollection.Config.XmlConfigurator.Configure( new  System.IO.FileInfo( "  .\\Configs\\ServerConfig.config  "  ));
        } 

   到此,对配置文件的优化就结束了,由于网上对配置文件的管理方案也不多,特写出来与大家分享,如果你有什么好的方案,大家也可以一起交流。欢迎吐槽...

  写的太high,快到一点了,赶快整理整理,来一把“找你妹”睡觉了,还好明天是星期五,心情还不错,不对,已经是星期五了,555。

附源码: https://files.cnblogs.com/bhtx/ConfigSolution.rar

 

 

分类:  项目相关

标签:  Config ,  配置文件管理 ,  反射机制 ,  C#

 早上到公司都习惯迅速浏览一下新闻,朝鲜宣布进入战斗准备状态 随时准备导弹攻击,中日钓鱼岛事件...,再和同事愤青一番,又想到 欧洲一个主教的墓志铭,他说:当我年轻的时候,我以为我可以改变世界,但是随著年龄的增加,我发现我没有这个能力,於是我决定我只改变我的国家,但是我并不能改变我的国家,到我老的时候,我就想改变我的家人,但是我发现我的家人也不愿意被我改变,我才恍然大悟,如果当初我改变自己……

  自上一篇项目中的配置文件优化方案后,觉得不太符合当前的业务,于是在领导的亲自指导下,有了这篇配置文件优化方案(二),主要介绍 Visual Studio 2010的一个配置文件插件ConfigurationSectionDesigner,该插件是基于.Net配置体系。下载安装即可,同时在网上还有该插件的源码,有兴趣也可研究研究。

  下面一步一步介绍插件的使用,也给自己做一个知识的积累,要实现配置文件分模块、分类展示,.Net配置体系中可以是一个Section一个模块,可以参照WCF的配置结构。例如对于下面的一堆配置,平时使用时也只是解析Xml,非常不方便,我们就利用这个插件把他们都放在一个Section里面,使用起来也很方便。

优化前配置项

View Code

 1     <add key="localIPV4Scope" value="10."/>
 2     <add key="tcpCommTimeout" value="60000"/>
 3     <add key="localIPV4Scope" value="10."/>
 4     <add key="encoding" value="UTF-8"/>
 5     <add key="output" value="C:\"/>
 6     <add key="style" value="Style1"/>
 7     <mainFrameService>
 8       <service name="SPD0" channel="SPDTCP" server="192.168.1.100:6005" servicetype=""/>
 9       <service name="SPD1" channel="SPDTCP" server="192.168.1.101:6005" servicetype=""/>
10       <service name="SPD2" channel="SPDTCP" server="192.168.1.102:8991" servicetype=""/>
11     </mainFrameService>
12   </appSettings> 

优化后配置项

View Code

 1 <MyConfig localIPV4Scope="10." 
 2           tcpCommTimeout="60000" 
 3           description="MyConfig">
 4   <systemConfig encoding="UTF-8"
 5                 output="C:\"
 6                 style="Style1"/>
 7    <mainFrameService>
 8       <service name="SPD0" channel="SPDTCP" server="192.168.1.100:6005" servicetype=""/>
 9       <service name="SPD1" channel="SPDTCP" server="192.168.1.101:6005" servicetype=""/>
10       <service name="SPD2" channel="SPDTCP" server="192.168.1.102:8991" servicetype=""/>
11    </mainFrameService>
12 </MyConfig> 

   一、首先Add New Item,找到刚才安装的插件自带的模版,命名为MyConfig.csd

   二、建立配置项模型,在新建的.csd文件左边的Tollbox中有相关的控件,首先拖一个ConfigurationSection命名为MyConfig,分别新增三个Attribute,在属性页分别选择Attribute的Type,再新增两个Element(SystemConfig、MainFrameService),对于SystemConfig,要拖一个ConfigurationElement命名为SystemConfigElement,并新增三个对应的Attribute,同时选择对应的Type,然后把上面SystemConfig的Type指定为SystemConfigElement,这样就关联在一起了,同时出现一个指向的箭头。然后就是MainFrameService了,该节点时一个集合,所以要拖一个ConfigurationElementCollection命名为MainFrameServiceCollection,再拖一个ConfigurationElement命名为Service,同时新增对应的Attribute和指定对应的Type.这样把MainFrameService的Type指定为MainFrameServiceCollection,MainFrameServiceCollection的Item Type指定为Service。配置模型就建立成功了,如下图,保存时,会生成对应的配置项代码。

   三、通过以上工作,我们建立了配置的基本结构,下面是配置的实现结果,如下:

View Code

 1   <configSections>
 2     <section name="myConfig" type="ConfigDesigner.MyConfig, ConfigDesigner"/>
 3   </configSections>
 4   <myConfig localIPV4Scope="10."
 5             tcpCommTimeout="60000"
 6             description="MyConfig">
 7     <systemConfig encoding="UTF-8"
 8                   output="C:\"
 9                   style="Style1"/>
10     <mainFrameService>
11       <service name="SPD0" channel="SPDTCP" server="192.168.1.100:6005" servicetype=""/>
12       <service name="SPD1" channel="SPDTCP" server="192.168.1.101:6005" servicetype=""/>
13       <service name="SPD2" channel="SPDTCP" server="192.168.1.102:8991" servicetype=""/>
14     </mainFrameService>
15   </myConfig> 

这样就算配置模块很多,config文件中也显得很清晰,也可以用configSource属性分几个外部配置文件分别配置。使用也很方便,如下:

View Code

 1 var localIPV4Scope = MyConfig.Instance.LocalIPV4Scope;
 2             var tcpCommTimeout = MyConfig.Instance.TcpCommTimeout;
 3             var encoding = MyConfig.Instance.SystemConfig.Encoding;
 4             var output = MyConfig.Instance.SystemConfig.Output;
 5             var style = MyConfig.Instance.SystemConfig.Style;
 6             Console.WriteLine("localIPV4Scope:{0}",localIPV4Scope);
 7             Console.WriteLine("tcpCommTimeout:{0}", tcpCommTimeout);
 8             Console.WriteLine("encoding:{0}", encoding);
 9             Console.WriteLine("output:{0}", output);
10             Console.WriteLine("style:{0}", style);
11 
12             var mainFrameService = MyConfig.Instance.MainFrameService;
13             foreach (Service service in mainFrameService)
14             {
15                 Console.WriteLine("name:{0},channel:{1},server:{2},servicetype:{3}", service.Name, service.Channel, service.Server, service.Servicetype);
16             }
17 
18             Console.ReadLine(); 

                                                        注重细节,从改变自己做起

  

 

 

分类:  项目相关

作者: Leo_wl

    

出处: http://www.cnblogs.com/Leo_wl/

    

本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

版权信息

查看更多关于项目中配置文件优化方案的详细内容...

  阅读:40次