想起了昨天老大交待的事.在修改DNT.config配置文件中的配置项时,程序无法检测到配置文件已经被修改.
小菜沉思了一会儿,想到了解决方案,使用一个定时器Timer,每隔一段时间让它重新加载一下基本配置描述类.
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>
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.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.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.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文件下,你得设计一个好点的架构,不要出现太多的重复代码
小菜本想好好去玩玩的,,,唉,,,希望破灭.......太伤心的小菜醒了过来......原来是做梦,,......(呵,,小菜准备去厕所,,,下篇继续做梦解决这个问题)