我们有一个插件系统,其中插件代码在主进程的单独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中检索东西(如果这有意义的话)。
答案 0 :(得分:9)
所以我的同事和我最终用Reflector的一堆帮助解决了这个问题。
主要问题是当通过远程调用访问时,HttpContext.Current为null。
HttpContext.Current属性存储在一个有趣的庄园中。一些嵌套的setter下来,你到达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
,<{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。