2011-02-01 103 views
5

我們有一個插件系統,其中插件代碼在主進程的獨立AppDomain上運行,使用.NET Remoting進行通信。.NET Remoting和HttpContext.Current

一類是類似於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上創建,並且工作正常。然後,還有插件類,這是它的應用程序域創建,並給予CommunicatingClass的一個實例:

public class PluginClass 
{ 
    public void DoSomething(ICommunicatingClass communicatingClass) 
    { 
     communicatingClass.DoSomething(); 
    } 
} 

的問題是,即使CommunicatingClass駐留在主應用程序域(與即時窗口驗證),所有的靜態數據,如MyClass.Instance和HttpContext.Current已經消失,並且爲空。我有一種感覺,MyClass.Instance以某種方式從插件AppDomain中檢索,但我不確定如何解決此問題。

我看到另一個問題,建議RemotingServices.Marshal,但這似乎沒有幫助,或者我用它不正確。有沒有一種方法可以使CommunicatingClass像AppDomain中的其他類一樣訪問所有靜態方法和屬性?

編輯:

的插件類的給定一個實例是這樣的:

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

如果你不知道:遠程處理,取而代之WCF的被棄用。 – 2011-02-01 02:40:26

+0

如果您包含用於在PluginClass應用程序域中獲取對communicationsClass的引用的代碼,它可能會有所幫助。 – JohnOpincar 2011-02-01 02:41:30

+0

@John Saunders,WCF如何與跨應用程序域通信一起工作?當客戶端和服務器分開時,它是有意義的,但在這種情況下,它看起來不像透明的遠程代理那麼簡單。我從來沒有使用WCF,只是看着它。 – Snea 2011-02-01 03:33:54

回答

9

因此,我的同事和我一起解決了這個問題,最後得到了來自Reflector的幫助。

主要問題是當通過遠程調用訪問HttpContext.Current時爲空。

HttpContext.Current屬性存儲在一個有趣的莊園。一些嵌套的setter下來,你達到CallContext.HostContext。這是一個CallContext上的靜態對象屬性。

設置Call​​Context時,它首先檢查值是否爲ILogicalThreadAffinitive

  • 如果是,則將值存儲在當前線程的LogicalCallContext中。
  • 如果不是,則會將值存儲在當前線程的IllogicalCallContext中。

HttpContext一個ILogicalThreadAffinitive,所以它被存儲在IllogicalCallContext


然後,有遠程處理。

我們並沒有深入瞭解它的來源,但是它的功能是從其他功能中推斷出來的。

當一個呼叫由遠程對象不同的應用程序域,該呼叫不是直接代理到原來的線程,在完全相同的執行環境中運行。

首先,原始線程(包含HttpContext.Current的線程)的ExecutionContext通過ExecutionContext.Capture(更多在此處)被捕獲。

然後,返回ExecutionContextCapture被作爲第一個參數傳遞給ExecutionContext.Run,esentially形成的代碼:

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。

但是,它可以穿越ExecutionContext沒有問題。

因此,在AppDomain的主要MarshalByRefObject中,作爲代理向另一個AppDomain提供,在構造函數中給它的實例爲HttpContext.Current

那麼,在新的對象(不幸)的每個方法調用,運行:

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

它會毫無問題地設置。由於HttpContext。目前與ExecutionContextIllogicalCallContext綁定,它不會流入ASP.NET可能創建的任何其他線程,並且在處置副本ExecutionContext時將被清除。

(我可能是錯的多這一點,雖然這是所有的猜測和思考。)

1

我相信你還需要從MarshalByRefObject派生MyClass。