.NET Remoting和HttpContext.Current

Sne*_*nea 5 .net c# remoting appdomain

我们有一个插件系统,其中插件代码在主进程的单独AppDomain上运行,使用.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;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

然后,我们有一个继承MarshalByRefObject的通信对象:

public class CommunicatingClass : MarshalByRefObject, ICommunicatingClass
{
    public void DoSomething()
    {
        MyClass.Instance.DoSomething();
    }
}
Run Code Online (Sandbox Code Playgroud)

CommunicatingClass是在主AppDomain上创建的,并且工作正常.然后,就是在其AppDomain上创建的插件类,并给出了CommunicatingClass的一个实例:

public class PluginClass
{
    public void DoSomething(ICommunicatingClass communicatingClass)
    {
        communicatingClass.DoSomething();
    }
}
Run Code Online (Sandbox Code Playgroud)

问题是,即使CommunicatingClass驻留在主appdomain上(使用立即窗口验证),所有静态数据(如MyClass.Instance和HttpContext.Current)都已消失,并且为null.我有一种感觉MyClass.Instance以某种方式从插件AppDomain中检索,但我不确定如何解决这个问题.

我看到了另一个提出的问题RemotingServices.Marshal,但这似乎没有帮助,或者我错误地使用了它.有没有一种方法可以让CommunicatingClass像主AppDomain中的任何其他类一样访问所有静态方法和属性?

编辑:

PluginClass给出了这样一个实例:

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

编辑2:

可能已经找到了问题的根源.MyClass.Instance存储在HttpContext.Current.Items中(参见上面的编辑).

HttpContext.Current可以访问正确的HttpContext吗?我仍然想知道为什么,即使它是在HttpContext.Current的AppDomain,CommunicatingClass.DoSomething中运行,当调用MyClass.Instance时,从PluginClass'AppDomain中检索东西(如果这有意义).

Sne*_*nea 9

所以我的同事和我最终用Reflector的一堆帮助解决了这个问题.

主要问题是当通过远程调用访问时,HttpContext.Current为null.

HttpContext.Current属性存储在一个有趣的庄园中.一些嵌套的setter下来,你到达CallContext.HostContext.这是一个静态对象属性CallContext.

设置Call​​Context后,它首先检查值是否为ILogicalThreadAffinitive.

  • 如果是,则将值存储在当前线程中LogicalCallContext.
  • 如果不是,则将值存储在当前线程中IllogicalCallContext.

HttpContext不是一个ILogicalThreadAffinitive,所以它被保存在IllogicalCallContext.


然后,有远程.

我们没有对其来源进行过多挖掘,但它的作用是从其他一些功能中推断出来的.

不同的AppDomain 远程对象进行调用时,该调用不会直接代理到原始线程,而是在完全相同的执行上下文中运行.

首先,通过(稍后更多)捕获ExecutionContext原始线程(包含的线程).HttpContext.CurrentExecutionContext.Capture

然后,ExecutionContext返回的from Capture作为第一个参数传递给ExecutionContext.Run,实际上形成了代码:

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

然后,完全透明地访问远程对象中的代码.

不幸的是,HttpContext.Current没有被捕获ExecutionContext.Capture().

这就是IllogicalCallContexta和a 之间的本质区别LogicalCallContext.

Capture创建一个全新的ExecutionContext,基本上将所有成员(例如LogicalCallContext)复制到新对象中.但是,它并没有复制IllogicalCallContext.

所以,既然HttpContext不是ILogicalThreadAffinative,它就无法被捕获ExecutionContext.Capture.


解决方案?

HttpContext不是MarshalByRefObject或[Serializable](可能有充分理由),因此无法传递给新的AppDomain.

但是,它可以ExecutionContext毫无问题地交叉.

因此,在主AppDomain的MarshalByRefObject中,它作为其他AppDomain的代理给出,在构造函数中给它实例HttpContext.Current.

然后,在每个方法调用新对象(不幸的是),运行:

private HttpContext _context;
private void SyncContext()
{
    if(HttpContext.Current == null)
        HttpContext.Current = _context;
}
Run Code Online (Sandbox Code Playgroud)

并且它将被设置没有问题.由于HttpContext.Current是联系在一起的IllogicalCallContextExecutionContext,也不会流血到该ASP.NET可能创建的任何其他线程,并将复印时将被清理ExecutionContext布置.

(尽管如此,我可能错了很多.这都是猜测和反思)