Как мне сообщить событие между доменами приложений? - PullRequest
5 голосов
/ 28 апреля 2011

У меня есть два домена приложений, один родитель создает дочерний домен.В дочернем домене есть объект 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 *Объект 1009 * предоставляет событие OnLoad, которое я хотел бы захватить в родительском домене.Если я просто добавляю делегат обработчика событий, он пытается сериализовать ScanningTask в дочерний домен и выдает исключение, что он не сериализуем.

Что я действительно хочу, так это чтобы событие передавалось через домены.Любые умные предложения о том, как?

1 Ответ

6 голосов
/ 03 мая 2011

Основываясь на этом решении , вы также можете сделать задачу класса Task наследованной от MarshalByRefObject.Это решит проблему с сериализацией, поскольку она передаст сериализованную ссылку между доменами приложений, которая будет использоваться для присоединения к событию.

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