如何分离web.config改进版本

      三年前我分享了如何分离web.config中的配置节,因为有些项目过大,造成N多配置节存在于web.config中,缺点如下:
      1:不容易管理,当你想查找一个配置节时,望着整页的code,不知所措,为此你只有ctrl+f来解决。
      2:部署时也及容易出错,部署人员需要按照你写的部署文档,一个一个加,即费时又容易出错,比如一不小心将其它节点给覆盖了诸如此类。
      3:在web.config中的配置节的修改会引起站点重启。
      4:访问配置节不够简单,容易出错。

      文章之前我提到过我们为了解决此种问题的解决方案,就是将配置节从web.config文件中分离出来,将配置节存入单独的文件中。具体的方案请参考前面的文章(如何分割web.config ),有很长一段时间没有使用了,最近在使用上发现在多项目中复用有一定问题,即每个项目都需要编写一段不短的代码,下面就是我对于它的优化过程,先看下原来的工作量:

      第一:Webconfig,这是框架里面的内容,这也是唯一得以复用的地方。它是一个入口,所有的配置文件引用都通过它,比如访问酒店的配置类,WebConfig.Hotel.HotelName。代码如下:    

View Code
public partial  class WebConfig
    {
        /// <summary>
        
/// 启动配置类
        
/// </summary>
       public static void OnStart(string configFilePath, FileUpdate fileUpdate)
        {
            #region  实现配置文件独立
            //第一次启动需要执行委托方法更新配置类
            fileUpdate(configFilePath);
            //启动文件监视
            Log4netFileWatchHelper.StartWatching(fileUpdate, configFilePath);
            #endregion
        }
        /// <summary>
        
/// Config/Web 文件夹的磁盘路径
        
/// </summary>
       public static string ConfigFilePathRoot
       {
           get;
           set;
       }       
    }


       1:包含一个重要方法OnStart,可以对配置文件进行初始化,其实这个初始化也可以省略掉,因为完全可以将初始化配置类变成延迟加载模式。
       2:一个配置文件路径的属性,它标识了此项目类所有配置文件的存放目录,可以在程序初始化时指定此属性。

     第二:自定义的配置文件类,比如我们可以添加一个数据访问的配置类。

 

View Code
 [Serializable ]
    public  class DataAccessConfig
    {   
        #region 需要序列化的配置文件属性
        /// <summary>
        
/// 数据库信息列表
        
/// </summary>
        public List<DataBase> DataBaseList { getset; }
        #endregion

        #region 相对于Config文件夹的子文件路径,不需要序列化.
        /// <summary>
        
/// 相对于Config文件夹的子文件路径,不需要序列化.
        
/// </summary>
        [NonSerialized()]
        private static string m_SubFilePath = @"DataAccessConfig.config";
        /// <summary>
        
/// 子文件路径(排除config文件夹路径后的部分)
        
/// </summary>
        public static string SubFilePath
        {
            get { return m_SubFilePath; }
            set { m_SubFilePath = value; }
        }
        #endregion
        public static DataAccessConfig CreateInstance()
        {
            FileUpdate fileUpdate = new FileUpdate(WebConfig.DataAccessConfigOnUpdate);
            string configFilePath = WebConfig.ConfigFilePathRoot + DataAccessConfig.SubFilePath;
            if (!File.Exists(configFilePath))
            {
                return null;
            }
            DataAccessConfig config = SerializationHelper.Load(typeof(DataAccessConfig), configFilePath) as DataAccessConfig;
            //启动文件监视
            Log4netFileWatchHelper.StartWatching(fileUpdate, configFilePath);
            return config;
        }     
    }
   
    public partial class WebConfig
    {
        #region 第二步:为DataAccessConfig类添加入口
        /// <summary>
        
/// 属性对应的私有变量
        
/// </summary>
        private static DataAccessConfig m_DataAccessConfig = null;
        /// <summary>
        
/// 属性访问器.通过WebConfig.SimpleFileDemoConfig可以访问此类.
        
/// </summary>
        public static DataAccessConfig DataAccessConfig
        {
            get
            {
                if (m_DataAccessConfig == null)
                    Interlocked.CompareExchange<DataAccessConfig>(ref m_DataAccessConfig,
                        DataAccessConfig.CreateInstance(), null);
                return m_DataAccessConfig;
            }
        }
        #endregion

        #region 第三步:为DataAccessConfig类添加更新函数
        /// <summary>
        
/// 更新函数
        
/// </summary>
        
/// <param name="status"></param>
        public static void DataAccessConfigOnUpdate(object status)
        {
            lock (WebConfig.DataAccessConfig)
            {
                try
                {
                    m_DataAccessConfig = DataAccessConfig.CreateInstance();
                }
                catch (Exception ex)
                {
                    throw ex;
                }
            }
        }
        #endregion
    }

 

       这段代码就是我们要实现的部分,不能复用,总觉的需要改进一下,问题分析:
       1:由于需要采用静态调用方式,为此要想通过WebConfig的静态属性调用,就需要在WebConfig类中增加自定义配置类的属性,比如我们增加一个Hotel相关的配置类,而要想WebConfig.Hotel,就需要增加一个静态属性方式实现。这里可以稍微修改下,即最后在配置自定义配置类时只编写自定义配置类,而不用去编写WebConfig类。
       2:自定义配置类中的CreateInstance,也要考虑复用。

       原则就是自定义的配置类不关心如何读取配置文件,只关心自己的配置属性即可。
       
       改善后的版本:
       1:针对WebConfig.自定义类方式。这里我修改的方法也不是最好的,用起来没有修改前的顺畅,但代码确实精简了。思路就是在WebConfig类中生成一个泛型配置类。
       

public partial class WebConfig<T> :WebConfig where T:class

 

       这里新增了WebConfig的泛型版本,而将WebConfig提取成基类,WebConfig类中包含一个属性,即配置文件所在文件夹路径。
      

 /// <summary>
        
/// Config/Web 文件夹的磁盘路径
        
/// </summary>
        public static string ConfigFilePathRoot
        {
            get;
            set;
        }

 

        WebConfig<T>主要目的是为了生成T类型的配置类,这里将原本在自定义配置类中的代码提取到WebConfig<T>中:

View Code
         /// <summary>
        
/// 属性对应的私有变量
        
/// </summary>
        private static T m_DataAccessConfig ;
        /// <summary>
        
/// 属性访问器.通过WebConfig.SimpleFileDemoConfig可以访问此类.
        
/// </summary>
        public static T DataAccessConfig
        {
            get
            {
                if (m_DataAccessConfig == null)
                    Interlocked.CompareExchange<T>(ref m_DataAccessConfig,
                        CreateInstance(), null );
                return m_DataAccessConfig;
            }
        }        
         /// <summary>
        
/// 更新函数
        
/// </summary>
        
/// <param name="status"></param>
        public static void DataAccessConfigOnUpdate(object status)
        {
            lock (WebConfig<T>.DataAccessConfig)
            {
                try
                {
                    m_DataAccessConfig = CreateInstance();
                }
                catch (Exception ex)
                {
                    throw ex;
                }
            }
        }

    
        2:将自定义配置类生成实例的方法也进行封装。
         

private static T CreateInstance()
        {
            SubFilePath = typeof(T).Name+".config";
            string configFilePath = WebConfig<T>.ConfigFilePathRoot + SubFilePath;
            if (!File.Exists(configFilePath))
            {
                return null;
            }
            T config = SerializationHelper.Load(typeof(T), configFilePath) as T;
            //启动文件监视
            FileWatch(configFilePath);
            return config;            

        }

        
        有了上面的提取封装,下面就是改版后的自定义类了,到目前为止,我们编写一个自定义配置类的成本已经非常低了:
        

[Serializable]
    public class MyConfig : WebConfig<DataAccessConfig>
    {
        #region 需要序列化的配置文件属性
        public string ExpenseTemplateFile { getset; }
        public bool Switch { getset; }
        #endregion      
    }

 

       最后我们添加一个配置文件,名称和自定义配置类保持一致。
      

  <?xml version="1.0" encoding="utf-8" ?>
<MyConfig>
  <ExpenseTemplateFile>ssssss3</ExpenseTemplateFile>
  <Switch>false</Switch>
</MyConfig>

    
      客户端调用方式:   

WebConfig<MyConfig>.DataAccessConfig.Switch

      

       虽然本次改版并不完美,但在多个项目中利用时还是起到了很大的作用,编写简单,调用方便,唯一不满意的就是需要这样写:WebConfig<MyConfig>,没有以前的WebConfig.MyConfig方式来的舒服。

posted on 2012-01-13 21:16  min.jiang  阅读(3870)  评论(2编辑  收藏  举报