2011-04-28 89 views
4

我有两个应用程序域,一个父域创建子域。在子域中,有一个MarshalByRef对象,使用.NET Remoting进行通信。在父域中运行的对象调用的远程对象的包装器作为应用程序的功能的一部分:如何在应用程序域之间传递事件?

public class ScanningTask : Task 
{ 
    private class Loader : MarshalByRef 
    { 
     public void Load(IEnumerable<string> paths) 
     { 
      ... 
     } 

     public event EventHandler<LoadEventArgs> OnLoad; 
    } 

    public void RunTask() 
    { 
     var domain = AppDomain.CreateDomain("LoadDomain"); 

     var loader = (Loader)domain.CreateInstanceFromAndUnwrap(
      typeof(Loader).Assembly.Location, 
      typeof(Loader).FullName); 

     loader.Load(...); 

     AppDomain.Unload(domain); 
    } 
} 

为简洁移除大多数代码。

This Loader object exposed an OnLoad event我想在父域中捕获。如果我只是添加一个事件处理程序委托,它会尝试将ScanningTask序列化到子域中,并引发关于它不可序列化的异常。

我真正想要的是事件在域之间传递。有什么聪明的建议如何?

回答

5

根据this solution,你可以让你的Task类任务继承自MarshalByRefObject。这将解决序列化问题,因为它会传递将用于附加到事件的跨AppDomain序列化引用。

public class ScanningTask : MarshalByRefObject 
{ 
    private class Loader : MarshalByRefObject 
    { 
     public void Load() 
     { 
      if (OnLoad != null) 
       OnLoad(this, EventArgs.Empty); 
     } 

     public event EventHandler OnLoad; 
    } 

    public void RunTask() 
    { 
     var domain = AppDomain.CreateDomain("LoadDomain"); 

     var loader = (Loader)domain.CreateInstanceFromAndUnwrap(
      typeof(Loader).Assembly.Location, 
      typeof(Loader).FullName); 

     loader.OnLoad += new EventHandler(loader_OnLoad); 
     loader.Load(); 

     AppDomain.Unload(domain); 
    } 

    void loader_OnLoad(object sender, EventArgs e) 
    { 
     Console.Write("load event called"); 
    } 
} 

如果现有的代码库的原因基类任务无法进行可从MarshalByRefObject继承的,您的解决方案可能是一个代理类,加载者(因此是一个MarshalByRefObject的本身)继承并调用转发到实际展开实例。

public class ScanningTask 
{ 
    private class Loader : MarshalByRefObject 
    { 
     public virtual void Load() 
     { 
      RaiseOnLoad(this); 
     } 

     protected void RaiseOnLoad(Loader loader) 
     { 
      if (OnLoad != null) 
       OnLoad(loader, EventArgs.Empty); 
     } 

     public event EventHandler OnLoad; 
    } 

    private class LoaderProxy : Loader 
    { 
     public readonly Loader Instance; 

     public LoaderProxy(Loader loaderInstance) 
     { 
      this.Instance = loaderInstance; 
      this.Instance.OnLoad += new EventHandler((sender, e) => RaiseOnLoad(this.Instance)); 
     } 

     public override void Load() 
     { 
      this.Instance.Load(); 
     } 
    } 

    public void RunTask() 
    { 
     var domain = AppDomain.CreateDomain("LoadDomain"); 

     var loader = (Loader)domain.CreateInstanceFromAndUnwrap(
      typeof(Loader).Assembly.Location, 
      typeof(Loader).FullName); 

     var proxy = new LoaderProxy(loader); 
     proxy.OnLoad += new EventHandler(loader_OnLoad); 
     loader.Load(); // same as proxy.Load() 

     AppDomain.Unload(domain); 
    } 

    void loader_OnLoad(object sender, EventArgs e) 
    { 
     Console.Write("load event called"); 
    } 
} 
+0

包装的任务工作作为一种解决方案,它工作得很好。尽管我确实遇到了解决依赖关系的问题;检查AppDomain.AssemblyResolve事件,如果你得到奇怪的,无法解释的错误! – 2011-05-04 10:28:31

+0

创建AppDomain时,传入AppDomainSetup的实例以配置您需要的任何程序集解析自定义。 我相信在上面的场景中,您的新AppDomain将基于当前可执行文件所在的目录。因此,如果你的动态加载的DLL不在同一个目录下,你会得到加载器错误。 因此,请记住将AppDomainSetup对象的ApplicationBase路径设置为包含所有必需依赖项的位置。 – Adam 2011-05-09 04:43:16

+0

- 记住,注册一个事件会导致'动态加载的dll'尝试加载'订阅dll',所以这两个dll必须可以在子AppDomain中解决。 – Adam 2011-05-09 04:48:12

相关问题