.NET Remoting和HttpContext.Current

时间:2011-02-01 02:25:42

标签: c# .net 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;
        }
    }
}

然后,我们有一个继承自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上(使用立即窗口验证),所有静态数据(如MyClass.Instance和HttpContext.Current)都已消失,并且为空。我有一种感觉,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;
}

编辑2:

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

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

2 个答案:

答案 0 :(得分:9)

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

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

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

设置Call​​Context后,它首先检查该值是否为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()

这是IllogicalCallContextLogicalCallContext之间的本质区别。

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

因此,由于HttpContext不是ILogicalThreadAffinative,<{1}}无法捕获


解决方案?

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

但是,它可以毫无问题地跨越ExecutionContext.Capture

因此,在主AppDomain的MarshalByRefObject中,它作为其他AppDomain的代理提供,在构造函数中为其提供ExecutionContext的实例。

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

HttpContext.Current

它将被设置没有问题。由于HttpContext.Current与private HttpContext _context; private void SyncContext() { if(HttpContext.Current == null) HttpContext.Current = _context; } 的{​​{1}}相关联,因此它不会渗透到ASP.NET可能创建的任何其他线程中,并且在复制IllogicalCallContext时将被清除。处置。

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

答案 1 :(得分:1)

我相信你也需要从MarshalByRefObject派生出MyClass。