真忙啊,好不容易来一次。 

       大家在开发过程中或多或少的会使用一些置配文件来存储一些应用程序的配置信息。虽然.net WinForm下有了App.Config,WebForm下有了Web.Config,但很多时候我们都会创建自己的配置文件,用来存储连接字符串或者存储其他信息。  

       好了,切入正题!如果你只使用.net提供的配置文件存储信息,下面内容唯一的作用就是浪费您的时间和伤害你的眼睛.  关于怎样使用配置文件不是本文的主要内容。我假设大家使用了自己的配置文件,因为配置文件中的内容变动不太频繁,为了提高效率,在程序运行期间,一般只会从配置文件中读取一次信息,然后将其存储在缓存或者一个单实体类中。如此当然是方便高效。如果程序是一个运行时间不长的工具软件,这一切都是美好的,不会出任何问题。 但如果程序是一个执行时间很长或者干脆就是一个后台程序,或者是某台服务器上的Web程序。那痛苦就开始了。由于配置信息只会获取一次,当你的配置信息发生变化时,程序中使用的配置信息还是之前的版本,你不得不手动重启程序来反应最新的变化。如果的程序在异地的一个服务器上,那就只有吐血的份了。  

      解决办法有N种, 下面,就是我采用的解决办法:  
      使用 FileSystemWatcher!!! 

     在我的的"自动更新程序"中已经使用了一个Config.xml 存储远端服务器的一个URL,每隔20秒钟,自动更新程序连接到配置文件中记录的URL上获取更新文件。如果每次扫描远程服务器时都读一次配置文件,将会没有必要地浪费性能。 我用一个Config类来获取配置信息。 Config类是一个单实例类,在程序运行期间,他只有一个实例,并且一直存在。在第一次使用他时,创建一个实例,并且将配置信息保存到对像里。以后,只从这个对像中获取信息而不去读取配置文件。下面是一个Config类的简化版:  

 

using System; 
 using System.IO; 
 using System.Xml; 
 namespace Unicom.Utility 
 { 
  /** <summary> 
  /// Config 的摘要说明。 
  /// </summary> 
  public class Config 
  { 
  //配置文件所在的路径 
  private const string FILE_PATH=@"\Data\config.xml"; 
  
  public static Config _Instance=null; //一个Config对像 
  public static Config Instance 
  { 
  get 
  { 
  //第一次使用,如果没有实例,创建一个 
  if(_Instance==null) 
  { 
  _Instance=new Config(); 
  } 
  
  return _Instance; 
  } 
  } 
  
  private string _URL=string.Empty; //存储URL信息. 
  public string URL 
  { 
  get 
  { 
  return _URL; 
  } 
  } 
  
  private FileSystemWatcher fileWatcher=null; 
  
  //读取配置信息 
  private string GetInfoFromFile() 
  { 
  //开始读取配置文件,如果指ConfigFilePath指定的是一个相对路径,则取当前应用程序下的相应路径, 
  //如果是绝对路径,采用绝对路径 
  XmlTextReader myXml=new XmlTextReader(Path.Combine(System.AppDomain.CurrentDomain.BaseDirectory,FILE_PATH)); 
  
  try 
  { 
  return myXml.ReadElementString("URL"); 
  } 
  catch(Exception ex) 
  { 
  throw ex; 
  } 
  finally 
  { 
  myXml.Close(); 
  } 
  } 
  
  private Config() 
  { 
  _URL=GetInfoFromFile(); //读取并保存xml文件中的配置信息 
  
  string FullPath=Path.Combine(System.AppDomain.CurrentDomain.BaseDirectory,FILE_PATH); 
  
  //监视指定的配置文件 
  fileWatcher=new FileSystemWatcher(Path.GetDirectoryName(FullPath),Path.GetFileName(FullPath)); 
  fileWatcher.Changed+=new FileSystemEventHandler(OnChange); 
  fileWatcher.NotifyFilter=NotifyFilters.LastWrite; //监视最后写入时间 
  fileWatcher.EnableRaisingEvents=true;//允许触发事件 
  } 
  
  //配置文件变化时,重新读取配置信息 
  private void OnChange() 
  { 
  _URL=GetInfoFromFile(); 
  } 
  
  } 
 }


 
           这个类同样会有我上面提到的问题,现在我们使用FileSystemWatcher 类来解决,在msdn中的描述如下:  
   使用 FileSystemWatcher 监视指定目录中的更改。可监视指定目录中的文件或子目录的更改。该组件可以监视本地计算机、网络驱动器或远程计算机上的文件。  当被监视的文件发生改动时,FileSystemWatcher会触发相应的事件,比如:修改,创建.  
     FileSystemWatcher的以下成员很关键:   Changed 事件 ,NotifyFilter 属性 。 NotifyFilter是一个枚举型成员,用来监视文件的更改种类,成员如下: 

成员名称 说明 值 
Attributes  文件或文件夹的属性。 4 
CreationTime  文件或文件夹的创建时间。 64 
DirectoryName  目录名。 2 
FileName  文件名。 1 
LastAccess  文件或文件夹上一次打开的日期。 32 
LastWrite  上一次向文件或文件夹写入内容的日期。 16 
Security  文件或文件夹的安全设置。 256 
Size  文件或文件夹的大小。 8 

    我们只需LastWrite 这一个就足够用了。 

        要让FileSystemWatcher 在文件变化时通知我们,并且执行指定的代码,我们需要将要执行的方法注册到FileSystemWatcher的 Change事件中,设置好其他属性。 我们先定义一个方法,当配置文件变化时,重新读取配置信息。 

      现在们来修改一个Config类。首先,添加一个成员 FileSystemWatcher: 
           private FileSystemWatcher fileWatcher=null; 
    然后,在构造函数中添加以下代码  

//监视指定的配置文件 
  string FullPath=Path.Combine(System.AppDomain.CurrentDomain.BaseDirectory,FILE_PATH); 
  fileWatcher=new FileSystemWatcher(Path.GetDirectoryName(FullPath),Path.GetFileName(FullPath)); 
  fileWatcher.Changed+=new FileSystemEventHandler(OnChange); 
  fileWatcher.NotifyFilter=NotifyFilters.LastWrite; //监视最后写入时间 
  fileWatcher.EnableRaisingEvents=true;//允许触发事件


  在类里面添加一个新的方法:OnChange,用来重新获取配置信息 

//配置文件变化时,重新读取配置信息 
  private void OnChange() 
  { 
  _URL=GetInfoFromFile(); 
  }


   好了,一切搞定,在Config.xml发生修改时,最新的配置信息会在第一时间加载到内存中。 痛苦解除了。 
     
    下面是修改后的完整的Config类 

   

using System; 
 using System.IO; 
 using System.Xml; 
 namespace Unicom.Utility 
 { 
  /** <summary> 
  /// Config 的摘要说明。 
  /// </summary> 
  public class Config 
  { 
  //配置文件所在的路径 
  private const string FILE_PATH=@"\Data\config.xml"; 
  
  public static Config _Instance=null; //一个Config对像 
  public static Config Instance 
  { 
  get 
  { 
  //第一次使用,如果没有实例,创建一个 
  if(_Instance==null) 
  { 
  _Instance=new Config(); 
  } 
  
  return _Instance; 
  } 
  } 
  
  private string _URL=string.Empty; //存储URL信息. 
  public string URL 
  { 
  get 
  { 
  return _URL; 
  } 
  } 
  
  private FileSystemWatcher fileWatcher=null; 
  
  //读取配置信息 
  private string GetInfoFromFile() 
  { 
  //开始读取配置文件,如果指ConfigFilePath指定的是一个相对路径,则取当前应用程序下的相应路径, 
  //如果是绝对路径,采用绝对路径 
  XmlTextReader myXml=new XmlTextReader(Path.Combine(System.AppDomain.CurrentDomain.BaseDirectory,FILE_PATH)); 
  
  try 
  { 
  return myXml.ReadElementString("URL"); 
  } 
  catch(Exception ex) 
  { 
  throw ex; 
  } 
  finally 
  { 
  myXml.Close(); 
  } 
  } 
  
  private Config() 
  { 
  _URL=GetInfoFromFile(); //读取并保存xml文件中的配置信息 
  
  //监视指定的配置文件 
  string FullPath=Path.Combine(System.AppDomain.CurrentDomain.BaseDirectory,FILE_PATH); 
  fileWatcher=new FileSystemWatcher(Path.GetDirectoryName(FullPath),Path.GetFileName(FullPath)); 
  fileWatcher.Changed+=new FileSystemEventHandler(OnChange); 
  fileWatcher.NotifyFilter=NotifyFilters.LastWrite; //监视最后写入时间 
  fileWatcher.EnableRaisingEvents=true;//允许触发事件 
  } 
  
  //配置文件变化时,重新读取配置信息 
  private void OnChange() 
  { 
  _URL=GetInfoFromFile(); 
  } 
  
  } 
 }