2011-02-01 3 views
5

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

Один класс похож на 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 находится на главной AppDomain (проверено с Immediate Window), все статические данные, такие как MyClass.Instance и HttpContext.Current, исчезли и имеют значение null. У меня такое ощущение, что MyClass.Instance каким-то образом извлекается из плагина AppDomain, но я не уверен, как это решить.

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

Edit:

PluginClass дается пример, как это:

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

Edit 2:

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

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

+0

В случае, если вы не знаете: Remoting является устаревшим в пользу WCF. –

+0

Это может помочь, если вы включили код, который вы используете, чтобы получить ссылку на communicationClass в домене приложения PluginClass. – JohnOpincar

+0

@ Джон Сондерс, Как работает WCF с перекрестной связью? Это имеет смысл, когда клиент и сервер разделены, но не похоже, что в этом случае было бы проще, чем прозрачные удаленные прокси. Я никогда не работал с WCF, а просто смотрел на него. – Snea

ответ

9

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

Основная проблема заключается в том, что 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, esentially формирования кода:

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] (вероятно, по уважительной причине), поэтому он не может быть передан новому AppDomain.

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

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

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

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

И он будет установлен без проблем. С HttpContext.Ток привязан к IllogicalCallContextExecutionContext, он не будет кровоточить в любые другие потоки, которые ASP.NET мог бы создать, и будет очищен, когда будет удалена копия ExecutionContext.

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

+0

Я думал, что ваша проблема была с MyClass, а не HttpContect.Current. Возможно, вы должны изменить свой вопрос. Или вы говорите в этом ответе, что MyClass все еще недоступен? – JohnOpincar

+0

MyClass хранился в HttpContext, я узнал. Я изменю название. – Snea

1

Я считаю, что вам нужно будет вывести MyClass из MarshalByRefObject.

+0

Да, все классы, которые зависят от конкретной ссылки на удаленный объект (т. Е. Одноэлемент в этом случае), должны также расширяться из 'MarshalByRefObject', иначе они теряются на другой стороне домена приложения. –

+0

Странно, я раньше пробовал, и у меня все та же проблема. Я буду играть с ним еще немного. Кроме того, почему это было бы? Если экземпляр CommunicatingClass находится на том же AppDomain, что и MyClass, разве это не будет видно прямо там? Или CommunicatingClass где-то «между» доменами? – Snea

+0

См. Выше edit. MyClass, являющийся MarshalByRefObject, вероятно, исправляет свой собственный случай, но HttpContext.Current не может быть MarshalByRefObject. – Snea