cdo

导航

完整的动态加载/卸载程序集的解决方案

转自:http://dlwang2002.cnblogs.com/archive/2005/10/31/265810.html

实现目标

1:所加载的dll分布在不同的文件夹下,可以不再运行目录bin下。以创建AppDomain的方式加载/卸载

2:运行中可以自动监测dll的版本,如果dll又更新,则自动卸载原来的dll,重新加载新的程序集(当然也就得必须可以替换正在运行中的dll)

3:加载程序集中的类可以访问主程序域的方法(主程序域中的类当然可以访问自程序域中的实例的方法)


优点:


1:可以在运行中替换具体实现,不需要停止程序再加载

2:在不同程序域里的实现互相隔离,代码运行比较安全,一个实现出了问题,可以只卸载那一个AppDomain就可以



几个问题:

1:子程序域中无法访问主程序域中的一些配置文件,比如app.config

2:运行速度会慢一些,因为需要反复的 序列化/反序列化 。

3:调试复杂,不同程序域的调试就像递归函数一样,难以调试

4:不再是传实例引用,所有的都是传值的方式。


实现思路

1:通过创建不同的AppDomain来加载dll,通过卸载AppDomain来卸载dll

2:通过一个Proxy类(继承自MarshalByRefObject)来访问具体的实现,一定不能返回具体实现的实例,而是要通过传递参数,在代理中执行,然后返回结果

3:子程序域中实现通过一个ProxyBack(MarshalByRefObject)的代理来访问主程序域中的方法,这个ProxyBack使用TCP信道来通讯 (Microsoft .Net Remoting)

4:所有需要在不同域之间传递的参数/返回值 都必须是可以序列化的。

5:制作一个DLLWatcher来监视dll所在文件夹的状态



具体实现

1:代理类 创建不同的程序域 访问子程序域中的方法

     
http://dlwang2002.cnblogs.com/archive/2005/10/18/257425.html

2:使用Microsoft .Net Remoting技术,制作ProxyBack

    关于Microsoft .Net Remoting技术,
Wayfarer's Prattle有很详细的解释http://www.cnblogs.com/wayfarer/archive/2004/07/30/28723.html

    这里主要看这几个主要的实现和注意事项


   这是代理的Server端,在乎程序域中启动,侦听
   public ProxyBackServer()
  {
   if(!isStart)
    {
      TcpChannel chan = new TcpChannel(8085);
       ChannelServices.RegisterChannel(chan);
 
      RemotingConfiguration.RegisterWellKnownServiceType(typeof( xxx.ProxyBackServer),
     "CallLocalSM",WellKnownObjectMode.SingleCall);
      isStart=true;
    }
   方法:注意所传递的都必须是可以序列化的对象,在需要加载dll的文件夹下也要有这个传递对象实现的程序集。
    public IStateDictionary RunService(IStateDictionary request)

   在这个Server的析构函数中要释放这个TCP信道

  ~ProxyBackServer()
//  {
//   IChannel[] channels = ChannelServices.RegisteredChannels;
//   //关闭指定名为CallLocalSM的通道;
//   foreach (IChannel eachChannel in channels)
//   {
//    if (eachChannel.ChannelName == "CallLocalSM")
//    {
//     TcpChannel tcpChannel = (TcpChannel)eachChannel;
//
//     //关闭监听;
//     tcpChannel.StopListening(null);
//
//     //注销通道;
//     ChannelServices.UnregisterChannel(tcpChannel);
//    }
//   }
//
//  }



     这是代理的Client端,子程序域访问主程序域的时候使用
   public ProxyBackClient()
  {
   if(!isReg)// boot once
   {
      TcpChannel chan = new TcpChannel();
      ChannelServices.RegisterChannel(chan);
       isReg=true;
   }
   _server = (ProxyBackServer)Activator.GetObject(
    typeof(xxxx.ProxyBackServer), "tcp://localhost:8085/CallLocalSM");
     if (_server == null) 
    {
      throw new Exception("can not connect to local host to get instance of sm");
     }

}
   public IContext RunService(IContext ctx)
  {
   try
    {
       ctx.Response= this._server.RunService(ctx.Request);
      return ctx;
..........

    }

}
   

 3:DllWatcher

   ............

   _mask = "*.dll";
   // initialize watcher to watch the process directory
   this.dllWatcher = new FileSystemWatcher();
   this.dllWatcher .Path = _path;
   this.dllWatcher .Filter = _mask;
   this.dllWatcher .Changed +=  new FileSystemEventHandler(File_OnChanged);
   this.dllWatcher .Created +=  new FileSystemEventHandler(File_OnChanged);
   this.dllWatcher .Deleted +=  new FileSystemEventHandler(File_OnChanged);
   // tell it to start watching
   this.dllWatcher .EnableRaisingEvents = true;

在接收到事件之后,调用主域中的方法卸载AppDomain,然后重新加载 就可以

posted on 2005-10-31 21:32  Cdo  阅读(607)  评论(0编辑  收藏  举报