閱讀63 返回首頁    go 技術社區[雲棲]


隻在UnitTest和WebHost中的出現的關於LogicalCallContext的嚴重問題

最近一直在進行公司內部框架的升級工作,其中一個小的部分就是通過HttpSessionState和CallContext建立一套統一的、可擴展的用於管理上下文信息的框架。在為寫好的程序編寫Unit Test和QuickStart的時候,遇到了兩個基於LogicalCallContext的嚴重問題。導致這兩個問題的根源還沒有來得及去追蹤,或許是微軟VS  Unit Test框架本身和WebHost本身的一個Bug。現在將其寫出來,一來是希望讀者在遇到相同情況的時候知道LogicalCallContext可能是影響因素之一,另一方麵也希望借助社區的力量快速找到問題的症結。以下內容假定讀者已經對CallContext有一個大概的了解,並且明白LogicalCallContext和IllogicalCallContext之間的區別。對此不了解的讀者,可以參考我的文章《如何實現對上下文(Context)數據的統一管理 》。

一、在VS Unit Test下設置LogicalCallContext導致的序列化問題

為了演示在Unit Test下設置LogicalCallContext會導致怎樣的問題,為此我寫了一個非常簡單的例子去重現它。首先我定義了如下一個形如Key-Value 的類型:LogicalContextItem<TValue>:

   1: [Serializable]
   2: public class LogicalContextItem<TValue>
   3: {
   4:     public string Key { get; private set; }
   5:     public TValue Value { get; set; }
   6:  
   7:     public LogicalContextItem(string key, TValue value)
   8:     {
   9:         if (string.IsNullOrEmpty(key))
  10:         {
  11:             throw new ArgumentNullException("key");
  12:         }
  13:         this.Key = key;
  14:         this.Value = value;
  15:     }
  16: }

然後通過如下一個TestMethod測試一個以LogicalCallContext的形式保存的上下文(通過調用CallContext的靜態方法LogicalSetData)是否可以通過相同的Key被正常獲取。

   1: [TestClass]
   2: public class LogicalSetDataFixture
   3: {
   4:     [TestMethod]
   5:     public void LogicaSetData()
   6:     {
   7:         LogicalContextItem<string> userName = new LogicalContextItem<string>("__userName", "Foo");
   8:         CallContext.LogicalSetData(userName.Key, userName);
   9:         Assert.AreEqual<string>("Foo", ((LogicalContextItem<string>)CallContext.LogicalGetData("__userName")).Value);
  10:     }
  11: }

但是運行上麵的UnitTest的時候,在TestResult對話框中會出現一個Error。從下圖中我們可以看出這是一個序列化的錯誤,具體的出錯信息為:Unit Test Adapter threw exception: Type is not resolved for member 'UnitTests.LogicalContextItem`1[[System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]],UnitTests, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null',即我們自定義的類型無法解析。這就有點讓人費解了,在這個實例中,LogicalContextItem<TValue>:類型本身是直接定義在UnitTest這個項目之中的,何來無法解析之理。image 

二、在VS ASP.NET WebHost下設置LogicalCallContext導致的序列化問題

我們知道,為了給基於ASP.NET的Web應用的開發者帶來便利,Visual Studio內置了一個簡單(和IIS比較而言)的Web應用承載工具,即WebHost。如果采用基於WebHost的承載方式(這是默認的承載方式),上麵的錯誤同樣會發生。為了演示,我們同樣使用上麵定義的LogicalContextItem<TValue>類型,然後在一個單純的WebPage中的Load事件處理方法中編寫了如下一段簡單的代碼:

   1: public partial class _Default : System.Web.UI.Page
   2: {
   3:     protected void Page_Load(object sender, EventArgs e)
   4:     {
   5:         LogicalContextItem<string> userNameContext = new LogicalContextItem<string>("__userName", "Foo");
   6:         CallContext.LogicalSetData(userNameContext.Key, userNameContext);
   7:         var userName = ((LogicalContextItem<string>)CallContext.LogicalGetData("__userName")).Value;
   8:         Response.Write(userName);
   9:     }
  10: }

直接運行上麵的程序,SerializationException異常照常被拋出,從下圖給出的錯誤對話框可以看出,該異常具有和上麵完全一樣的錯誤信息,即不能解析我們自定義的LogicalContextItem<TValue>類型。

image

為了找出拋出SerializationException異常的根源,我們可以來該異常的StatckTrace。從下麵給出的內容,我們可以看出來該異常最終就是通過WebHost拋出來得。

   1: at Microsoft.VisualStudio.WebHost.Host.ProcessRequest(Connection conn)
   2: at Microsoft.VisualStudio.WebHost.Server.OnSocketAccept(Object acceptedSocket)
   3: at System.Threading.QueueUserWorkItemCallback.WaitCallback_Context(Object state)
   4: at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean ignoreSyncCtx)
   5: at System.Threading.QueueUserWorkItemCallback.System.Threading.IThreadPoolWorkItem.ExecuteWorkItem()
   6: at System.Threading.ThreadPoolWorkQueue.Dispatch()
   7: at System.Threading._ThreadPoolWaitCallback.PerformWaitCallback()

三、采用IIS承載我們的Web應用可以解決上述問題

為什麼我說這個問題隻和內置於VS中的Web應用承載工具WebHost有關呢?一來是因為上麵給出的異常StackTrace已經明顯反映了異常最後總就是從WebHost跑出來的。另一個主要的原因就是,如果我直接采用IIS來承載的話,運行上述的代碼後一切正常。有興趣的讀者可以從這裏下載實例程序進行試驗。你隻需要該Web應用進行了如下配置,放棄使用Vistual Studio Development Server,而選用Local IIS Web Server即可。

image



作者:蔣金楠
微信公眾賬號:大內老A
微博:www.weibo.com/artech
如果你想及時得到個人撰寫文章以及著作的消息推送,或者想看看個人推薦的技術資料,可以掃描左邊二維碼(或者長按識別二維碼)關注個人公眾號(原來公眾帳號蔣金楠的自媒體將會停用)。
本文版權歸作者和博客園共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁麵明顯位置給出原文連接,否則保留追究法律責任的權利。
原文鏈接

最後更新:2017-10-27 14:34:12

  上一篇:go  Unity.Interception System (2.0)
  下一篇:go  一個關於解決序列化問題的編程技巧