博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

小菜梦游Discuz!NT (第三篇 配置文件处理2)

Posted on 2008-04-29 22:16  a-peng  阅读(964)  评论(11编辑  收藏  举报
小菜又开始做梦了....睡的好香...
想起了昨天老大交待的事.在修改DNT.config配置文件中的配置项时,程序无法检测到配置文件已经被修改.
小菜沉思了一会儿,想到了解决方案,使用一个定时器Timer,每隔一段时间让它重新加载一下基本配置描述类.
using System;
using System.IO;
using System.Web;
using System.Xml.Serialization;

namespace Discuz.Config
{
    
/// <summary>
    
/// 基本配置文件管理类
    
/// </summary>

    public class BaseConfigFileManager
    
{
        
private static BaseConfigInfo m_configInfo; //基本配置信息描述对象 
        private static string m_configFilePath; //基本配置文件路径
        private static System.Timers.Timer timer = new System.Timers.Timer(); //定时器

        
static BaseConfigFileManager()
        
{
            m_configInfo 
= DeserializeInfo();

            timer.Enabled 
= true//该值指示 Timer 是否应引发 Elapsed 事件 
            timer.Interval = 15000//获取或设置引发 Elapsed 事件的间隔 1.5秒
            timer.AutoReset = true//该值指示 Timer 是应在每次指定的间隔结束时引发 Elapsed 事件,还是仅在指定的间隔第一次结束后引发该事件
            timer.Elapsed += new System.Timers.ElapsedEventHandler(Timer_Elapsed);
            timer.Start(); 
//通过将 Enabled 设置为 true 开始引发 Elapsed 事件
        }


        
/// <summary>
        
/// 每隔1.5秒重新加载基本配置描述信息
        
/// </summary>
        
/// <param name="sender"></param>
        
/// <param name="e"></param>

        protected static void Timer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
        
{
            m_configInfo 
= DeserializeInfo();
        }


        
/// <summary>
        
/// 基本配置文件路径
        
/// </summary>

        private static string ConfigFilePath
        
{
            
get
            
{
                
if (m_configFilePath == null)
                
{
                    HttpContext context 
= HttpContext.Current;
                    m_configFilePath 
= context.Server.MapPath("~/DNT.config");
                }


                
return m_configFilePath;
            }

        }


        
/// <summary>
        
/// 返回基本配置文件信息描述对象
        
/// </summary>

        private static BaseConfigInfo GetBaseConfig
        
{
            
get
            
{
                
return m_configInfo;
            }

        }


        
/// <summary>
        
/// 返回数据库类型
        
/// </summary>

        public static string GetDbType
        
{
            
get
            
{
                
return GetBaseConfig.DbType;
            }

        }


        
/// <summary>
        
/// 反序列化基本配置信息描述类
        
/// </summary>
        
/// <returns></returns>

        private static BaseConfigInfo DeserializeInfo()
        
{
            BaseConfigInfo configInfo;
            FileStream fs 
= null;
            
try
            
{
                fs 
= new FileStream(ConfigFilePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
                XmlSerializer serializer 
= new XmlSerializer(typeof(BaseConfigInfo));
                configInfo 
= (BaseConfigInfo)serializer.Deserialize(fs);
            }

            
catch (Exception ex)
            
{
                
throw ex;
            }

            
finally
            
{
                
if (fs != null)
                    fs.Close();
            }


            
return configInfo;
        }

    }

}

小菜马上对这个解决方案进行了测试Response.Write(Discuz.Config.BaseConfigFileManager.GetDbType);运行...
看到的是SqlServer.然后把DNT.config配置文件中的<DbType>SqlServer</DbType>
修改为<DbType>Access</DbType>
等待1.5秒,然后刷新了下页面,显示Access...哈哈...成功啦...

老大这时走了过来,看小菜的任务完成了如何,看了下代码.问到ConfigFilePath是否需要改成如下这种形式.

/// <summary>
/// 基本配置文件路径
/// </summary>

private static string ConfigFilePath
{
    
get
    
{
        
if (m_configFilePath == null)
        
{
            HttpContext context 
= HttpContext.Current;
            
if (context != null)
            
{
                m_configFilePath 
= context.Server.MapPath("~/DNT.config");
            }

            
else
            
{
                m_configFilePath 
= Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "DNT.config");
            }

        }


        
return m_configFilePath;
    }

}

小菜想了想,老大的这段代码和自己的有何不同.
唯一的差别就在于if(context!=null){/*来自Web的请求*/}else{/*不是来自Web的请求*/}

什么情况下会出现不是来自Web的请求呢? 当请求是由Web服务器自己发出的时候.
在我们的代码中是否会出现这种情况呢? 确实会出现,小菜使用定时器每隔1.5秒就引发了Elapsed事件.
处理代码是Timer_Elapsed-----间接就是--------m_configInfo = DeserializeInfo();
而DeserializeInfo()中又用到了ConfigFilePath所以,也就是说是应该使用老大说的代码...

可是小菜又想了,我刚才没有用老大的代码也一样正确的运行啊,这是什么原因呢?
因为第一次请求是来自Web的请求,为m_configFilePath赋值,它又是static类型,一直保存着,隔了1.5秒Timer_Elapsed中的代码被执行.使用到ConfigFilePath属性时,由于m_configFilePath不为null所以if(m_configFilePath!=null){/*这里不会被执行到了*/} 而是直接return m_configFilePath

所以说老大说的代码不是必须的......老大满意的点点头...

老大对小菜的工作感到挺满意.不过老大为了培养小菜,提出了更高的要求了.
小菜啊!你看你的代码,你每隔1.5秒,就取一次BaseConfigInfo.如果我一年没有修改这个DNT.config配置文件,你这一年时间内取了多少次BaeConfigInfo啊....小菜算了下,一年=365天=365*24小时=365*24*60分钟=365*24*60*60/1.5次
天呐,这数字真恐怖...

小菜赶紧跑回去修改代码了....小菜想到了解决方法了..通过System.IO.File.GetLastWriteTime(ConfigFilePath)
就可以得到最后修改文件的时间了,如果这个时间与新的最后修改文件的时间相同时,就表示该配置文件没有被修改..好,行动

using System;
using System.IO;
using System.Web;
using System.Xml.Serialization;

namespace Discuz.Config
{
    
/// <summary>
    
/// 基本配置文件管理类
    
/// </summary>

    public class BaseConfigFileManager
    
{
        
private static BaseConfigInfo m_configInfo; //基本配置信息描述对象
        private static string m_configFilePath; //基本配置文件路径
        private static System.Timers.Timer timer = new System.Timers.Timer(); //定时器
        private static DateTime m_fileOldChange; //基本配置文件最后修改时间

        
static BaseConfigFileManager()
        
{
            m_configInfo 
= LoadConfig(false);
            m_fileOldChange 
= File.GetLastWriteTime(ConfigFilePath);

            timer.Enabled 
= true//该值指示 Timer 是否应引发 Elapsed 事件 
            timer.Interval = 15000//获取或设置引发 Elapsed 事件的间隔 1.5秒
            timer.AutoReset = true//该值指示 Timer 是应在每次指定的间隔结束时引发 Elapsed 事件,还是仅在指定的间隔第一次结束后引发该事件
            timer.Elapsed += new System.Timers.ElapsedEventHandler(Timer_Elapsed);
            timer.Start(); 
//通过将 Enabled 设置为 true 开始引发 Elapsed 事件
        }


        
/// <summary>
        
/// 每隔1.5秒重新加载基本配置描述信息
        
/// </summary>
        
/// <param name="sender"></param>
        
/// <param name="e"></param>

        protected static void Timer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
        
{
            LoadConfig(
true);
        }


        
/// <summary>
        
/// 加载(反序列化)基本配置信息描述对象
        
/// </summary>
        
/// <param name="checkTime">是否检查并更新传递进来的"文件加载时间"变量</param>
        
/// <returns></returns>

        protected static BaseConfigInfo LoadConfig(bool checkTime)
        
{
            
if (checkTime)
            
{
                DateTime m_fileNewChange 
= File.GetLastWriteTime(ConfigFilePath);
                
if (m_fileOldChange != m_fileNewChange)
                
{
                    m_fileOldChange 
= m_fileNewChange;

                    m_configInfo 
= DeserializeInfo();
                }

            }

            
else
            
{
                m_configInfo 
= DeserializeInfo();
            }


            
return m_configInfo;
        }


        
/// <summary>
        
/// 基本配置文件路径
        
/// </summary>

        private static string ConfigFilePath
        
{
            
get
            
{
                
if (m_configFilePath == null)
                
{
                    HttpContext context 
= HttpContext.Current;
                    m_configFilePath 
= context.Server.MapPath("~/DNT.config");
                }


                
return m_configFilePath;
            }

        }


        
/// <summary>
        
/// 返回基本配置信息描述对象
        
/// </summary>

        private static BaseConfigInfo GetBaseConfig
        
{
            
get
            
{
                
return m_configInfo;
            }

        }


        
/// <summary>
        
/// 返回数据库类型
        
/// </summary>

        public static string GetDbType
        
{
            
get
            
{
                
return GetBaseConfig.DbType;
            }

        }


        
/// <summary>
        
/// 反序列化基本配置信息描述类
        
/// </summary>
        
/// <returns></returns>

        private static BaseConfigInfo DeserializeInfo()
        
{
            BaseConfigInfo configInfo;
            FileStream fs 
= null;
            
try
            
{
                fs 
= new FileStream(ConfigFilePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
                XmlSerializer serializer 
= new XmlSerializer(typeof(BaseConfigInfo));
                configInfo 
= (BaseConfigInfo)serializer.Deserialize(fs);
            }

            
catch (Exception ex)
            
{
                
throw ex;
            }

            
finally
            
{
                
if (fs != null)
                    fs.Close();
            }


            
return configInfo;
        }

    }

}

小菜开始在幻想老大表扬自己.哈哈...
老大过来晃了晃小菜的脑袋,小样在想啥呢?赶快做事..小菜把完成的代码给了老大过目,,老大看了下说不错,有进步了.

不过很明显使用了定时器之后,代码变的没有之前那么清晰了。
小菜看到了Can Can的留言:提示说System.IO.FileSystemWatcher会让代码比较清晰,决定看看这个组件.
SDK2.0文档可是我们的好朋友噢,查查它看下.从类名我们可以清晰的明白.这是一个文件系统监测者.
要是有个东东,可以在我指定的文件出现修改或删除等等时会通知我,那就好了.FileSystemWatcher便是这个东东
接下来就来看小菜的第一次尝试

using System;
using System.IO;
using System.Web;
using System.Xml.Serialization;

namespace Discuz.Config
{
    
/// <summary>
    
/// 基本配置文件管理类
    
/// </summary>

    public class BaseConfigFileManager
    
{
        
private static BaseConfigInfo m_configInfo; //基本配置信息描述对象
        private static string m_configFilePath; //基本配置文件路径
        private static FileSystemWatcher watcher = new FileSystemWatcher(); //文件监测者

        
static BaseConfigFileManager()
        
{
            m_configInfo 
= DeserializeInfo();
            
            watcher.NotifyFilter 
= NotifyFilters.LastWrite; //监测条件 上一次向文件或文件夹写入内容的日期。  
            watcher.Path = AppDomain.CurrentDomain.BaseDirectory; //监测目录
            watcher.Filter = "DNT.config"//监测文件
            watcher.Changed += new FileSystemEventHandler(OnChanged); //修改时委托调用OnChanged
            watcher.EnableRaisingEvents = true;//开始监测
        }


        
/// <summary>
        
/// 监测DNT.config配置文件,当被修改时调用
        
/// </summary>
        
/// <param name="sender"></param>
        
/// <param name="e"></param>

        private static void OnChanged(object sender, FileSystemEventArgs e)
        
{
            m_configInfo 
= DeserializeInfo();
        }


        
/// <summary>
        
/// 基本配置文件路径
        
/// </summary>

        private static string ConfigFilePath
        
{
            
get
            
{
                
if (m_configFilePath == null)
                
{
                    HttpContext context 
= HttpContext.Current;
                    m_configFilePath 
= context.Server.MapPath("~/DNT.config");
                }


                
return m_configFilePath;
            }

        }


        
/// <summary>
        
/// 返回基本配置信息描述对象
        
/// </summary>

        private static BaseConfigInfo GetBaseConfig
        
{
            
get
            
{
                
return m_configInfo;
            }

        }


        
/// <summary>
        
/// 返回数据库类型
        
/// </summary>

        public static string GetDbType
        
{
            
get
            
{
                
return GetBaseConfig.DbType;
            }

        }


        
/// <summary>
        
/// 反序列化基本配置信息描述类
        
/// </summary>
        
/// <returns></returns>

        private static BaseConfigInfo DeserializeInfo()
        
{
            BaseConfigInfo configInfo;
            FileStream fs 
= null;
            
try
            
{
                fs 
= new FileStream(ConfigFilePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
                XmlSerializer serializer 
= new XmlSerializer(typeof(BaseConfigInfo));
                configInfo 
= (BaseConfigInfo)serializer.Deserialize(fs);
            }

            
catch (Exception ex)
            
{
                
throw ex;
            }

            
finally
            
{
                
if (fs != null)
                    fs.Close();
            }


            
return configInfo;
        }

    }

}

小菜成功用上了,FileSystemWatcher,代码清晰度提高了一些.
    但是这句代码有点不化不类. watcher.Path = AppDomain.CurrentDomain.BaseDirectory; //监测目录
这时老大也走了过来,看了下小菜的代码.      

说到:小菜啊,你有听说重构吗?
有啊,最近重构和测试挺热门的。就是让我们的代码更有质量啊。
不错,有关注it的发展。
    1.你看代码到处是"DNT.config".
    2.还有一点就是如果DNT.config不存在那怎么办啊。
不过还是得肯定下你最近的进步。儒鸟可教也。
小菜决定对代码好好修理一下,重构要及时的进行,不要拖太久,否则会越欠越多,到后期代码可就很难维护了。


using System;
using System.IO;
using System.Web;
using System.Xml.Serialization;

namespace Discuz.Config
{
    
/// <summary>
    
/// 基本配置文件管理类
    
/// </summary>

    public class BaseConfigFileManager 
    
{
        
private static BaseConfigInfo m_configInfo; //基本配置信息描述对象
        private static string m_configFileName = "DNT.config"//基本配置文件名称
        private static string m_configDirectory; //基本配置文件所在目录
        private static string m_configFilePath; //基本配置文件路径
        private static FileSystemWatcher watcher; //文件监测者

        
static BaseConfigFileManager()
        
{
            m_configInfo 
= DeserializeInfo();

            watcher 
= new FileSystemWatcher();
            watcher.NotifyFilter 
= NotifyFilters.LastWrite; //监测上一次向文件写入内容的日期
            watcher.Path = ConfigDirectory; //监测目录
            watcher.Filter = m_configFileName; //监测文件
            watcher.Changed += new FileSystemEventHandler(OnChanged);//监测文件被修改时委托调用OnChanged
            watcher.EnableRaisingEvents = true//开始监测
        }


        
/// <summary>
        
/// 基本配置文件所在目录
        
/// </summary>

        private static string ConfigDirectory
        
{
            
get
            
{
                
if (m_configDirectory == null)
                
{
                    m_configDirectory 
= ConfigFilePath.Substring(0, ConfigFilePath.LastIndexOf('\\'));
                }


                
return m_configDirectory;
            }

        }


        
/// <summary>
        
/// 基本配置文件路径
        
/// </summary>

        private static string ConfigFilePath
        
{
            
get
            
{
                
if (m_configFilePath == null)
                
{
                    HttpContext current 
= HttpContext.Current;
                    m_configFilePath 
= current.Server.MapPath("~/" + m_configFileName);

                    
if (!File.Exists(m_configFilePath))
                    
{
                        
throw new Exception("发生错误: 网站根目录下没有正确的DNT.config文件");
                    }

                }


                
return m_configFilePath;
            }

        }


        
/// <summary>
        
/// 返回数据库类型
        
/// </summary>

        public static string GetDbType
        
{
            
get
            
{
                
return m_configInfo.DbType;
            }

        }


        
/// <summary>
        
/// 监测文件被修改时委托调用OnChanged
        
/// </summary>
        
/// <param name="sender"></param>
        
/// <param name="e"></param>

        protected static void OnChanged(object sender, FileSystemEventArgs e)
        
{
            m_configInfo 
= DeserializeInfo();
        }


        
/// <summary>
        
/// 反序列化基本配置信息描述类
        
/// </summary>
        
/// <returns></returns>

        public static BaseConfigInfo DeserializeInfo()
        
{
            BaseConfigInfo configInfo;
            FileStream fs 
= null;
            
try
            
{
                fs 
= new FileStream(ConfigFilePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
                XmlSerializer serializer 
= new XmlSerializer(typeof(BaseConfigInfo));
                configInfo 
= (BaseConfigInfo)serializer.Deserialize(fs);
            }

            
catch (Exception ex)
            
{
                
throw ex;
            }

            
finally
            
{
                
if (fs != null)
                    fs.Close();
            }


            
return configInfo;
        }

    }

}

老大这时走了过来,看了小菜的代码,连赞不错,不错。

对了小菜,有件事要和你说下,我刚才发现在我的办公桌上还有几个配置文件上次忘了给你,只给了你一个DNT.config
还有general.config通用配置文件,email.config邮箱配置文件,space.config个人空间配置文件,album.config相册配置文件等等,这些配置文件夹将放在论坛的config文件下,你得设计一个好点的架构,不要出现太多的重复代码

小菜本想好好去玩玩的,,,唉,,,希望破灭.......太伤心的小菜醒了过来......原来是做梦,,......(呵,,小菜准备去厕所,,,下篇继续做梦解决这个问题)