Pergunta

I have two app domains, one parent creating the child domain. In the child domain, there is a MarshalByRef object, being communicated with using .NET Remoting. An object running in the parent domain invokes the wrapper for the remote object as part of the application's function:

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);
    }
}

Most code removed for brevity.

This Loader object exposes an OnLoad event I'd like to capture in the parent domain. If I just add an event handler delegate, it tries to serialize the ScanningTask into the child domain and throws an exception about it not being serializable.

What I really want is for the event to be communicated across the domains. Any clever suggestions as to how?

Foi útil?

Solução

Based on this solution, you could make your Task class task inherit from MarshalByRefObject as well. This would solve the serialization issue as it would pass a cross-AppDomain serialized reference which would be used to attach to the event.

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");
    }
}

If for existing codebase reasons the base class Task cannot be made to inherit from MarshalByRefObject, your solution could be a proxy class that inherits from Loader (therefore being a MarshalByRefObject itself) and forwards calls to an actual unwrapped instance.

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");
    }
}
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top