实现目的:动态加载dll,执行完毕之后可以随时卸载掉,并可以替换这些dll,以在运行中更新dll中的类。


其实就是通过应用程序域AppDomain加载和卸载程序集。


具体实现起来倒也不难,我的问题是:可以加载了,可以卸载了,但是不同域的实例是通过代理来实现调用的(这是可以动态加载/卸载的基础),从主域调用子域中的没问题,反之如何让子域中的类来访问主域中的实例呢?



先说动态加载和卸载,前面两篇文章已经有很精彩的论述了,只说几个要点


1:主域中创建子域,加载的dll来自别的文件夹


   AppDomain svcDomain = null;

    try

    {

     AppDomainSetup setup = new AppDomainSetup();

     setup.ApplicationBase = AppDomain.CurrentDomain.BaseDirectory;

     setup.PrivateBinPath = setup.ApplicationBase;

      setup.ApplicationName = asmName.FullName;

      setup.ShadowCopyDirectories = setup.ApplicationBase+@"dlls";    //dll不是来自bin目录下(很奇怪,为什么有很多人认为dll必须就在bin目录下)      setup.ShadowCopyFiles = "true";

      svcDomain = AppDomain.CreateDomain(asmName.FullName, null, setup);

    }

    catch (Exception ex)

    {

         Log.Write("test","can not CreateDomain")

           return;

    }

    // Get remote service handler

    ServiceProxy svc = null;

    try

    { //关键点

       svc = (ServiceProxy)   svcDomain.CreateInstanceFromAndUnwrap(

      svcDomain.BaseDirectory + "\\Services.dll",    "MaServices.ServiceProxy");

    }

    catch (Exception ex)

    {

      AppDomain.Unload(svcDomain);

     Log.Write("test", "can not create proxy")

          return;

    }


2:proxy的定义,一定要继承自MarshalByRefObject,传递的数据对象一定要可以序列化

    public class ServiceProxy: MarshalByRefObject,IDisposable


3:proxy中加载dll


public bool LoadService(string assemblyName)

  {

   Assembly assembly = null;

   try

   {     assembly = Assembly.LoadFrom(assemblyName);

         IService sev = (IService)assembly.CreateInstance(typename, true);


         this.services[assemblyName]=sev ;

   }

 4:proxy中调用具体的类:注意,具体调用一定不能再主域中

public IContext RunService(string serviceName,IContext ctx)

  {

       object o = this.service[serviceName];

        ((IService)o).RunService(ctx);

        return ctx;

   }



     最后,主域中直接调用既可


   proxy.RunService(xx,xx)




就这些。这样程序就可以随时加载一个dll(位置任意),反射得到类,运行,这个dll随时可以被替换


几个关键点:
代理不要返回被反射的实例,传递的数据要可以序列化,注意设置ShadowCopyDirectories。

现在言归正传,说说问题


假设主域M,子域C1,C2。通过代理之后,M可以调用C1和C2中的实例,反过来,如何让C1调用M中的实例呢?或者C1调用C2中的实例?


tcp通讯是一种方法,但是系统消耗太大,频繁使用那就糟透了。


谁有高招?


还有一个问题:看上述文章时发现,他们的prxoy在返回时都是返回的要代理的类的实例,对于这一点我比较怀疑。如果dll不在一个目录下,那么本地将无法接受这个实例。所以要求本地必须有这么一个dll存在,才能接受这个返回结果,然后根据本地的dll中定义的类的结构来反序列化。这样一来,不论如何使用proxy,其实在DefaultAppDomain里面都引用了具体的类和dll,所以导致dll是被当前程序占用的,无论如何都不能被替换。


欢迎讨论。