我有两个应用域,一个父级创建子域。在子域中,有一个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);
}
}
为简洁起见,删除了大部分代码。
此Loader
对象公开了我想要在父域中捕获的OnLoad
事件。如果我只是添加一个事件处理程序委托,它会尝试将ScanningTask
序列化到子域中,并抛出一个关于它不可序列化的异常。
我真正想要的是让事件跨域传播。关于如何做出任何聪明的建议?
答案 0 :(得分:6)
基于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");
}
}
如果由于现有的代码库原因,无法使基类Task继承MarshalByRefObject,那么您的解决方案可能是从Loader继承的代理类(因此是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");
}
}