【问题标题】:.NET Remoting and HttpContext.Current.NET 远程处理和 HttpContext.Current
【发布时间】:2011-02-01 02:25:42
【问题描述】:

我们有一个插件系统,其中插件代码运行在与主进程不同的 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 中检索东西(如果这有意义的话)。

【问题讨论】:

  • 如果您不知道:远程处理已被弃用,取而代之的是 WCF。
  • 如果您在 PluginClass 应用程序域中包含用于获取通信类引用的代码,这可能会有所帮助。
  • @John Saunders,WCF 如何与跨应用程序域通信一起工作?当客户端和服务器分开时是有意义的,但在这种情况下似乎不像透明远程代理那么容易。不过,我从未使用过 WCF,只是看了看。
  • @Snea:它有效。它完全取代了远程处理。
  • @John Saunders,您是否有文章或其他问题谈论如何使用远程处理将跨应用程序域通信迁移到 WCF,或者我应该在这个程度上问另一个问题?

标签: c# .net remoting appdomain


【解决方案1】:

所以我和我的同事终于在 Reflector 的帮助下解决了这个问题。

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

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

设置 CallContext 时,它首先检查值是否为ILogicalThreadAffinitive

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

HttpContext不是ILogicalThreadAffinitive,所以它存储在IllogicalCallContext中。


然后是远程处理。

我们没有深入挖掘它的来源,但它的作用是从其他一些函数中推断出来的。

当调用远程对象来自不同的AppDomain时,调用不会直接代理到原始线程,在完全相同的执行上下文中运行。

首先,通过ExecutionContext.Capture 捕获原始线程的ExecutionContext(包含HttpContext.Current)(稍后会详细介绍)。

然后,从Capture 返回的ExecutionContext 作为第一个参数传递给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,它不能ExecutionContext.Capture 捕获。


解决方案?

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

但是,它可以毫无问题地穿越ExecutionContexts。

因此,在作为另一个 AppDomain 的代理的主 AppDomain 的 MarshalByRefObject 中,在构造函数中为其提供 HttpContext.Current 的实例。

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

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

它会毫无问题地设置。由于 HttpContext.Current 与 ExecutionContextIllogicalCallContext 相关联,因此它不会渗入 ASP.NET 可能创建的任何其他线程,并且会在处理副本 ExecutionContext 时被清除。

(不过,我可能在很多方面都错了。这都是猜测和反思)

【讨论】:

  • 我认为您的问题出在 MyClass,而不是 HttpContect.Current。也许你应该修改你的问题。还是您在此答案中说 MyClass 仍然无法访问?
  • MyClass 存储在 HttpContext 中,我发现了。我改一下标题。
  • 很好的解释。您是否将 HttpContext.Current 作为 appDomain.CreateInstanceAndUnwrap 调用中的参数传递给 MarshalByRefObject 的构造函数?或者您是在创建 MarshalByRefObject 的一个实例,然后以某种方式将其传递给另一个 AppDomain?
【解决方案2】:

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

【讨论】:

  • 是的,所有依赖于对远程对象的特定引用的类(即本例中的单例)也需要从MarshalByRefObject 扩展,否则它们会在另一端丢失应用域。
  • 奇怪,我之前尝试过,但仍然遇到同样的问题。我会再摆弄它一些。还有,为什么会这样?如果 CommunicatingClass 实例与 MyClass 位于同一个 AppDomain 上,它不会在那里可见吗?还是 CommunicatingClass 是“介于”域之间的某个地方?
  • 见上面的编辑。 MyClass 作为 MarshalByRefObject 可能确实解决了它自己的情况,但 HttpContext.Current 不能是 MarshalByRefObject。
  • 此时,您应该开始质疑您的设计。 MyClass 不是静态的吗?如果是这样,它不会存储在 HttpContext.Current 中。
  • 我确定这里存在设计问题。现在的问题是要么从 CommunicatingClass “访问” HttpContext.Current,要么找到另一种方法从 CommunicatingClass 访问 MyClass.Current。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-10-22
  • 2010-09-05
  • 2013-01-09
  • 2023-04-08
  • 1970-01-01
  • 2011-12-24
相关资源
最近更新 更多