Итак, мой коллега и я решили это, наконец, с помощью справки из 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
.
(хотя я могу ошибаться во многом из-за этого. Это все домыслы и размышления)