как организованы данные в разных AppDomains и как я могу получить к ним доступ через домены

Привет, у меня есть приложение, состоящее из некоторого «ядра» и модулей, которые динамически загружаются в собственный домен приложения и выгружаются по мере необходимости. Но я не понимаю одного. Я опубликую простой код, похожий на то, что у меня есть.

Существует «интерфейс» класса, который содержит множество виртуальных хуков и свойств, а также некоторые собственные функции:

[Serializable()]
public abstract class Module : MarshalByRefObject
{
    public static List<Module> module = new List<Module>();
    public string Name = "";
    public string Version = "unknown";
    public DateTime Date = DateTime.Now;

    Module()
    {
        lock(module)
        {
            module.Add(this);
        }
    }

    ~Module()
    {
        Exit();
        lock (module)
        {
            if (module.Contains(this))
            {
                module.Remove(this);
            }
        }
        core.Log("Module was unloaded: " + this.Name);
    }

    public virtual void Hook1()
    {
      // by default ignore
    }
}

каждый модуль представляет собой новый проект, который ссылается на ядро ​​и создает новый класс, унаследованный от модуля и расширяющий «ядро». Некоторые события перехватываются таким образом, что я иду в цикле через список Module.module, который должен содержать все существующие загруженные экземпляры, и вызываю соответствующий хук в этом. Это отлично работает, если я нахожусь в одном AppDomain. Но если я использую отдельный AppDomain для модуля, кажется, что память исходного домена копируется в новый домен, и когда я вызываю конструктор нового экземпляра, он не вставляет себя в статический массив в Module.module исходного домена, но он вставляет себя в Module.module в новом домене. Это означает, что в ядре у меня все еще 0 модулей в Module.module. Мне удалось исправить это, создав другую функцию, которая регистрирует модуль вместо той, что была у меня в конструкторе. Но все же бывает, что иногда я обращаюсь к памяти в исходном домене, а иногда к памяти в новом домене модуля. Это вызывает неприятности. Как я могу убедиться, что я всегда обращаюсь к одной и той же памяти из ядра и из модуля?

Пример того, что мне нужно сделать, это:

  • Передать указатель на экземпляр класса, который находится в памяти AppDomain A, на хук модуля, находящегося в AppDomain B.
  • пусть модуль что-то меняет в этом классе (в памяти домена А, а не в Б)

person Petr    schedule 13.11.2012    source источник
comment
Почему вы пометили класс как Serializable? Объекты, производные от MarshalByRerObject, — это объекты, которые находятся в одном домене приложения и доступны через прокси-серверы (неявно созданные с помощью .NET Remoting) из другого домена приложения. Атрибут Serializable следует добавить ко всем классам, объекты которых передаются из одного AppDomain в другой. Эти объекты обычно являются параметрами или возвращаемыми значениями методов MarshalByRefObjects. Кроме того, если вы добавите код, который создает/обращается к объекту модуля, вам будет легче получить помощь.   -  person Panos Rontogiannis    schedule 14.11.2012
comment
@PanosRontogiannis Я хочу иметь доступ к удаленным статическим членам модуля, таким как модуль List‹›; из ядра и других модулей, по этой причине я считаю, что он должен быть сериализуемым, а также другие объекты, которые содержат элементы, которые должны быть междоменными.   -  person Petr    schedule 15.11.2012


Ответы (2)


Вам потребуется создавать экземпляры прокси для каждого из целевых доменов и передавать их через границы домена приложения. Я бы создал шаблон контроллера, который управляет всеми модулями внутри каждого удаленного домена из текущего и передает прокси через границы домена, когда вам нужно манипулировать данными.

Ниже приведен пример управления объектом в нескольких доменах с помощью AppDomainToolkit.

namespace ConsoleApplication1
{
    using System;
    using System.Threading.Tasks;
    using AppDomainToolkit;
    using System.Collections.Generic;

    class Program
    {
        static void Main(string[] args)
        {
            using (var context1 = AppDomainContext.Create())
            using (var context2 = AppDomainContext.Create())
            using (var context3 = AppDomainContext.Create())
            {
                var pizzaInContext1 = Remote<Pizza>.CreateProxy(context1.Domain, "Hawaiian");

                // manipulate object from current app domain
                pizzaInContext1.RemoteObject.AddTopping(new Topping("Cheese"));

                // manipulate object from context 2
                RemoteAction.Invoke(
                    context2.Domain,
                    pizzaInContext1.RemoteObject,
                    new Topping("Pineapple"),
                    (pizza, topping) =>
                    {
                        pizza.AddTopping(topping);
                    });

                // manipulate object, adding info from context 3
                RemoteAction.Invoke(
                    context3.Domain,
                    pizzaInContext1.RemoteObject,
                    (pizza) =>
                    {
                        pizza.AddTopping(new Topping("Ham"));
                    });

                Console.WriteLine(pizzaInContext1.RemoteObject.ToString());
            }

            Console.ReadKey();
        }
    }

    class Pizza : MarshalByRefObject
    {
        private readonly IList<Topping> toppings;

        public Pizza(string name)
        {
            this.Name = name;
            this.toppings = new List<Topping>();
        }

        public string Name { get; private set; }

        public void AddTopping(Topping topping)
        {
            this.toppings.Add(topping);
        }

        public override string ToString()
        {
            var pizza = this.Name + " with toppings: [";
            for (int i = 0; i < this.toppings.Count; i++)
            {
                pizza += this.toppings[i].Name;
                if (i < this.toppings.Count - 1)
                {
                    pizza += ",";
                }
            }
            pizza += "]";
            return pizza;
        }
    }

    [Serializable]
    class Topping
    {
        public Topping(string name)
        {
            this.Name = name;
        }

        public string Name { get; private set; }

        public override string ToString()
        {
            return this.Name;
        }
    }
}

Вывод

person Jduv    schedule 26.11.2012

Статические данные не используются доменами приложений! Каждый AppDomain будет иметь свой список модулей.

person Panos Rontogiannis    schedule 15.11.2012
comment
это то, что я понял, но как я мог поделиться ими? - person Petr; 16.11.2012
comment
Я думаю, вы должны добавить хотя бы еще один слой в свой дизайн. Я бы переместил список загруженных модулей в другой класс на стороне ядра и управлял бы всем оттуда. Я бы также посоветовал вам прочитать книгу о .NET Remoting, поскольку вы выбрали ее для взаимодействия между доменами приложений. Начните с проверки времени жизни и проблем с активацией. - person Panos Rontogiannis; 16.11.2012