ASP.NET:在MVC中如何使用Session?
【IT168技術】本文將介紹Asp.net MVC 中的Session及基於現有代碼的處理方法。
相關閱讀:
Asp.net平台作為底層的框架,它提供了HttpContext.Session這個成員屬性讓我們可以方便地使用Session,但是在MVC中,Controller抽象類為也提供了這樣一個屬性,我們只要訪問它就可以了(支持更好的測試性)。
回想一下,前面我們看到SessionStateModule是根據當前HttpHandler來決定是不是啟用Session。但是現在 Controller和Page是分開的, Controller又是如何使用Session的呢?要回答這個問題就要扯到路由了,簡單地說:現在在MVC處理請求的時候,當前 HttpHandler是 MvcHandler類的實例,它有如下定義:
因此,在Controller.Session中,它是訪問的HttpContext.Session,而MvcHandler實現了 IRequiresSessionState接口,所以,訪問HttpContext.Session就可以獲取到Session 。注意哦,我上面的代碼取自MVC 2.0,從類型實現的接口可以看出,Session將一直有效,不能關閉,而且屬於影響並發的那種模式。所以,此時你只能從web.config中全局關 閉它。
說明,在MVC 3.0 和Asp.net 4.0中,才可以支持Controller訂制Session的訪問性。
在這種使用方式下,如果您不想繼續使用Session,可以使用上面我列出的替代方法。
在MVC中,還有一個地方也在使用Session,那就是Controller.TempData這個成員屬性。通常我們可能會這樣使用它:
return RedirectToAction( " Index " );
在這種地方,這些保存到TempData的數據其實也是存放在Session中的。你可以從web.config中關閉Session,你就能看到異常 了。對於這種使用方法,你仍然可以前面的替代方法,但是,還有另一種方法也能做為替代Session的方法。我們看一下Controller的一段代碼:
return new SessionStateTempDataProvider();
}
TempData就是通過這種Provider的方式來支持其它的保存途徑。而且在MvcFutures中,還有一個 CookieTempDataProvider類可供使用。使用也很簡單,獲取MVC源碼,編譯項目MvcFutures,然后引用它,重寫以上虛方法就 可以了:
{
return new Microsoft.Web.Mvc.CookieTempDataProvider(this.HttpContext);
}
注意哦,這里有2個陷阱:MVC 2的MvcFutures的CookieTempDataProvider並不能正常工作。至於我在嘗試時,發現它是這樣寫的(注釋部分是我加的):
{
byte [] bytes = Convert.FromBase64String(base64EncodedSerializedTempData);
var memStream = new MemoryStream(bytes);
var binFormatter = new BinaryFormatter();
return binFormatter.Deserialize(memStream, null) as TempDataDictionary; // 這里會導致一直返回null
// return binFormatter.Deserialize(memStream, null) as IDictionary < string , object > ; // 這樣寫才對嘛。
}
{
try {
return ( new JavaScriptSerializer()).Deserialize < IDictionary < string , object >> (
HttpUtility.UrlDecode(base64EncodedSerializedTempData));
}
catch {
return null;
}
}
public static string SerializeToBase64EncodedString(IDictionary < string , object > values)
{
if ( values == null || values.Count == 0 )
return null;
return HttpUtility.UrlEncode(
( new JavaScriptSerializer()).Serialize(values));
}
上面的方法雖然解決了序列化結果過長的問題,但它也引入了新的問題:由於使用IDictionary類型,造成復雜類型在序列化時就丟失了它們的類型信息,因此,在反序列化時,就不能還原正原的類型。也正是因為此原因,這種方法將只適合保存簡單基元類型數據。
現有的代碼怎么辦
本來,這篇博客到這里就沒有了。是啊,批也批過了,解決辦法也給了,還有什么好說的,不過,突然想到一個很現實的問題,要是有人問我:Fish,我的代 碼很多的地方在使用Session,如果按你前面的方法,雖可行,但是要改動的代碼比較多,而且需要測試,還要重新部署,這個工作量太大了,有沒有更好的 辦法?
是啊,這個還真是個現實的問題。怎么辦呢?
針對這個問題,我也認真的思考過,也回憶過曾經如何使用 Session,以及用Session都做過些什么。一般說來,用Session基本上也就是保存一些與用戶相關的臨時信息,而且不同的頁面使用的 Session沖突的可能性也是極小的,使用方式以 mode="InProc" 為主。其實也就是Cache,只是方便了與“當前用戶”的關聯而已。
於是針對這個前提,繼續想:現在要克服的最大障礙是並發的鎖定問題。至於這個問題嘛,我們可以參考一下前面MSND中的說明,就是因為GetItemExclusive這些方法搞出來的嘛。想到這里,似乎辦法也就有了:我也來實現一個使用Cache的Provider,並且在具體實現時,故意不搞鎖定,不就行了嘛。
最終,我提供二個Provider,它們都是去掉了鎖定相關的操作,試了一下,並發問題不存了。但有個問題需要說明一 下,ProcCacheSessionStateStore采用Cache保存Session的內容,與 mode="InProc" 類似, CookieSessionStateStore則采用Cookie保存Session對象,但它有個限制,只適合保存簡單基元類型數據(且不包含敏感信 息),原因與CookieTempDataProvider一樣。所以,請根據您的使用場景來選擇合適的Provider
以下是使用方法:很簡單,只要在web.config中加一段以下配置就好了:
< providers >
< add name = " ProcCacheSessionStateStore " type = " Fish.SampleCode.ProcCacheSessionStateStore " />
< add name = " CookieSessionStateStore " type = " Fish.SampleCode.CookieSessionStateStore " />
</ providers >
</ sessionState >
好了,這次不用改代碼了,在部署環境中,也只需要修改了一下配置就完事了。
警告:我提供的這二個Provider只是做了簡單的測試,並沒經過實際的項目檢驗,如果您需要使用,請自行測試它的可用性。