C# .NET远程处理和HttpContext.Current

C# .NET远程处理和HttpContext.Current,c#,.net,remoting,appdomain,C#,.net,Remoting,Appdomain,我们有一个插件系统,其中插件代码运行在与主进程分离的AppDomain上,使用.NET远程处理对象进行通信 有一个类类似于HttpContext.Current(它也有问题)(编辑,实际实现): 然后,我们有一个从MarshalByRefObject继承的通信对象: public class CommunicatingClass : MarshalByRefObject, ICommunicatingClass { public void DoSomething() {

我们有一个插件系统,其中插件代码运行在与主进程分离的AppDomain上,使用.NET远程处理对象进行通信

有一个类类似于HttpContext.Current(它也有问题)(编辑,实际实现):

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

public class CommunicatingClass : MarshalByRefObject, ICommunicatingClass
{
    public void DoSomething()
    {
        MyClass.Instance.DoSomething();
    }
}
通信类是在主AppDomain上创建的,可以正常工作。然后是plugin类,它是在AppDomain上创建的,并给出了CommunicationClass的一个实例:

public class PluginClass
{
    public void DoSomething(ICommunicatingClass communicatingClass)
    {
        communicatingClass.DoSomething();
    }
}
问题是,即使CommunicationClass驻留在主appdomain上(通过即时窗口验证),所有静态数据(如MyClass.Instance和HttpContext.Current)都已消失且为空。我感觉MyClass.Instance是从插件AppDomain中检索出来的,但我不确定如何解决这个问题

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

编辑:

PluginClass的实例如下所示:

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

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


有没有办法让HttpContext.Current访问正确的HttpContext?我仍然想知道为什么,即使它是在HttpContext中运行的。Current的AppDomain,CommunicationingClass.DoSomething,在调用MyClass.Instance时,从PluginClass的AppDomain检索内容(如果有意义的话)。

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

所以我的同事和我解决了这个问题,最后,在Reflector的帮助下

主要问题是当通过远程调用访问HttpContext.Current时,它是空的

HttpContext.Current属性存储在一个有趣的庄园中。通过几个嵌套的setter,您可以到达
CallContext.HostContext
。这是
CallContext
上的静态对象属性

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

  • 如果是,则将该值存储在当前线程的
    LogicalCallContext
  • 如果不是,则将该值存储在当前线程的
    不合逻辑的callcontext
HttpContext
不是一个
ILogicalThreadAffinitive
,因此它存储在
iLogicalCallContext


然后是远程处理。

我们没有深入研究它的源代码,但它的功能是从其他函数推断出来的

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

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

然后,从
Capture
返回的
ExecutionContext
作为第一个参数传递给
ExecutionContext。运行
,本质上形成代码:

Delegate myRemoteCall; //Assigned somewhere else in remoting
ExecutionContext.Run(ExecutionContext.Capture(), x => { myRemoteCall() }, null);
然后,完全透明地访问远程对象中的代码

不幸的是,
HttpContext.Current
未在
ExecutionContext.Capture()中捕获

这里是
不合逻辑的调用上下文
逻辑调用上下文
之间的本质区别

Capture
创建一个全新的
ExecutionContext
,基本上将所有成员(如
LogicalCallContext
)复制到新对象中。但是,它不会复制不合逻辑的调用上下文

因此,由于
HttpContext
不是
iLogicThreadAffinative
,因此它不能被
ExecutionContext.Capture
捕获


解决方案?

HttpContext不是MarshallByRefObject或[Serializable](可能是出于正当理由),因此无法将其传递到新的AppDomain

但是,它可以毫无问题地跨
执行上下文

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

然后,在新对象的每个方法调用中(很遗憾),运行:

它将毫无问题地被设定。由于HttpContext.Current与
ExecutionContext
IllogicalCallContext
绑定,因此它不会渗入到ASP.NET可能创建的任何其他线程中,并将在释放副本
ExecutionContext
时被清除


(不过,我在这一点上可能大错特错。这都是猜测和反思)

如果你不知道:远程处理已经被弃用,取而代之的是WCF。如果你在PluginClass应用程序域中包含用于获取对CommunicationClass的引用的代码,可能会有所帮助。@John Saunders,WCF如何与跨应用程序域通信协同工作?当客户机和服务器分开时,这是有意义的,但在这种情况下,它似乎不像透明的远程代理那么容易。我从来没有使用过WCF,只是看看它。@Snea:它很管用。它完全取代了远程处理。@John Saunders,您是否有关于如何使用远程处理将跨appdomain通信迁移到WCF的文章或其他问题,或者我应该问另一个这样的问题?是的,所有依赖于对远程对象的特定引用的类(即本例中的单例类)还需要从
MarshallByRefObject
扩展,否则它们会在应用程序域的另一端丢失
Delegate myRemoteCall; //Assigned somewhere else in remoting
ExecutionContext.Run(ExecutionContext.Capture(), x => { myRemoteCall() }, null);
private HttpContext _context;
private void SyncContext()
{
    if(HttpContext.Current == null)
        HttpContext.Current = _context;
}