.NET Remoting и HttpContext.Current - PullRequest
       37

.NET Remoting и HttpContext.Current

5 голосов
/ 01 февраля 2011

У нас есть система плагинов, в которой код плагина выполняется на отдельном домене приложений от основного процесса, используя удаленное взаимодействие .NET для взаимодействия объектов.

Один класс похож на HttpContext.Current (который также страдает от проблемы) (редактирование, фактическая реализация):

public class MyClass
{
    public static MyClass Instance
    {
        get
        {
            if(HttpContext.Current != null)
                return HttpContext.Current.Items["MyClassInstance"];
        }
        set
        {
            if(HttpContext.Current != null)
                HttpContext.Current.Items["MyClassInstance"] = value;
        }
    }
}

Затем у нас есть связывающийся объект, который наследуется от MarshalByRefObject:

public class CommunicatingClass : MarshalByRefObject, ICommunicatingClass
{
    public void DoSomething()
    {
        MyClass.Instance.DoSomething();
    }
}

CommunicatingClass создается на главном AppDomain и работает нормально. Затем есть класс плагина, который создается на его AppDomain и получает экземпляр класса CommunicatingClass:

public class PluginClass
{
    public void DoSomething(ICommunicatingClass communicatingClass)
    {
        communicatingClass.DoSomething();
    }
}

Проблема в том, что, хотя CommunicatingClass находится на главном домене приложения (проверено с помощью окна Immediate Window), все статические данные, такие как MyClass.Instance и HttpContext.Current, исчезли и являются нулевыми. У меня такое ощущение, что MyClass.Instance каким-то образом извлекается из плагина AppDomain, но я не уверен, как решить эту проблему.

Я видел другой вопрос, который предлагал RemotingServices.Marshal, но, похоже, это не помогло, или я использовал его неправильно. Есть ли способ, которым CommunicatingClass может получить доступ ко всем статическим методам и свойствам, как и любой другой класс в основном домене приложений?

Edit:

Классу PluginClass присваивается следующий пример:

public static PluginClass Create()
{
    var appDomain = GetNewAppDomain();
    var instance = (PluginClass)appDomain.CreateInstanceAndUnwrap(assembly, type);
    instance.Communicator = new CommunicatingClass();
    return instance;
}

Редактировать 2:

Возможно, найден источник проблемы. MyClass.Instance хранится в HttpContext.Current.Items (см. Выше редактирование).

Есть ли вообще какой-нибудь способ, которым HttpContext.Current может получить доступ к правильному HttpContext? Мне все еще интересно, почему, хотя он и запускается в AppDomain HttpContext.Current, CommunicatingClass.DoSomething при вызове MyClass.Instance извлекает данные из AppDomain PluginClass (если это имеет какой-то смысл).

Ответы [ 2 ]

9 голосов
/ 03 февраля 2011

Итак, мой коллега и я решили это, наконец, с помощью справки из Reflector.

Основная проблема заключается в том, что HttpContext.Current имеет значение null при доступе через удаленный вызов.

Свойство HttpContext.Current хранится в интересной усадьбе.Несколько вложенных сеттеров вниз, вы достигаете CallContext.HostContext.Это свойство статического объекта в CallContext.

Когда CallContext установлен, он сначала проверяет, является ли значение ILogicalThreadAffinitive.

  • Если это так, он сохраняет значение в текущемLogicalCallContext.
  • потока. Если это не так, он сохраняет значение в текущем потоке IllogicalCallContext.

HttpContext равно , а не и ILogicalThreadAffinitive, поэтому он хранится в IllogicalCallContext.


Затем происходит удаленное взаимодействие.

Мы не слишком углубились в его источник,но то, что он делает, было выведено из некоторых других функций.

Когда выполняется вызов на удаленный объект из другого AppDomain, вызов не передается напрямуюисходный поток, работающий в том же контексте выполнения.

Во-первых, ExecutionContext исходного потока (тот, который содержит HttpContext.Current) перехватывается через ExecutionContext.Capture (подробнее об этом чуть позже).

Затем ExecutionContext, возвращаемый из Capture, передается в качестве первого аргумента ExecutionContext.Run, по существу формируя код:

Delegate myRemoteCall; //Assigned somewhere else in remoting
ExecutionContext.Run(ExecutionContext.Capture(), x => { myRemoteCall() }, null);

Затем полностьюпрозрачно, ваш код в удаленном объекте доступен.

К сожалению, HttpContext.Current не записывается в ExecutionContext.Capture().

В этом и заключается существенная разница между IllogicalCallContext и LogicalCallContext.

Capture создает совершенно новый ExecutionContext, по сути копируя все элементы (например, LogicalCallContext) в новый объект.Но не копирует IllogicalCallContext.

Итак, поскольку HttpContext не является ILogicalThreadAffinative, не может быть захвачено ExecutionContext.Capture.


Решение?

HttpContext не является MarshalByRefObject или [Serializable] (вероятно, по уважительной причине), поэтому его нельзя передать в новый домен приложения.

Но , этоможет пересечь ExecutionContext с без проблем.

Итак, в главном AppDomain MarshalByRefObject, который передается в качестве прокси другому AppDomain, в конструкторе присваивают ему экземпляр HttpContext.Current.

Затем в каждом вызове методановый объект (к сожалению), запустите:

private HttpContext _context;
private void SyncContext()
{
    if(HttpContext.Current == null)
        HttpContext.Current = _context;
}

И он будет установлен без проблем.Поскольку HttpContext.Current связан с IllogicalCallContext из ExecutionContext, он не попадет ни в какие другие потоки, которые может создать ASP.NET, и будет очищен при удалении копии ExecutionContext.

(хотя я могу ошибаться во многом из-за этого. Это все домыслы и размышления)

1 голос
/ 01 февраля 2011

Полагаю, вам нужно будет также извлечь MyClass из MarshalByRefObject.

...