ASP.NET:在MVC中如何使用Session?

2011年12月16日11:30 来源:博客 作者:Fish Li 编辑:苏巧红我要评论(0)

标签: ASP.NET , .NET , web开发

查看全文

  【IT168技术】本文将介绍Asp.net MVC 中的Session及基于现有代码的处理方法。

  相关阅读:

  ASP.NET:Session的缺点总结及解决方法

  ASP.NET:Session对并发访问的影响

  ASP.NET:session的来龙去脉解析

  Asp.net平台作为底层的框架,它提供了HttpContext.Session这个成员属性让我们可以方便地使用Session,但是在MVC中,Controller抽象类为也提供了这样一个属性,我们只要访问它就可以了(支持更好的测试性)。

   回想一下,前面我们看到SessionStateModule是根据当前HttpHandler来决定是不是启用Session。但是现在 Controller和Page是分开的, Controller又是如何使用Session的呢?要回答这个问题就要扯到路由了,简单地说:现在在MVC处理请求的时候,当前 HttpHandler是 MvcHandler类的实例,它有如下定义:

publicclass MvcHandler : IHttpAsyncHandler, IHttpHandler, IRequiresSessionState {

   因此,在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这个成员属性。通常我们可能会这样使用它:

TempData["mydata"] ="aaaaaaaaaa"; //or other object

return RedirectToAction("Index");

   在这种地方,这些保存到TempData的数据其实也是存放在Session中的。你可以从web.config中关闭Session,你就能看到异常 了。对于这种使用方法,你仍然可以前面的替代方法,但是,还有另一种方法也能做为替代Session的方法。我们看一下Controller的一段代码:

protected virtual ITempDataProvider CreateTempDataProvider() {

returnnew SessionStateTempDataProvider();

}

   TempData就是通过这种Provider的方式来支持其它的保存途径。而且在MvcFutures中,还有一个 CookieTempDataProvider类可供使用。使用也很简单,获取MVC源码,编译项目MvcFutures,然后引用它,重写以上虚方法就 可以了:

protected override ITempDataProvider CreateTempDataProvider()

{

returnnew Microsoft.Web.Mvc.CookieTempDataProvider(this.HttpContext);

}

  注意哦,这里有2个陷阱:MVC 2的MvcFutures的CookieTempDataProvider并不能正常工作。至于我在尝试时,发现它是这样写的(注释部分是我加的):

publicstatic IDictionary<string, object> DeserializeTempData(string base64EncodedSerializedTempData)

{

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>; // 这样写才对嘛。

}

就算能运行,这样做会导致生成的Cookie的长度较大,因此容易导致浏览器不支持。最终我重写了以上代码(以及另一个序列化的代码):

publicstatic IDictionary<string, object> DeserializeTempData(string base64EncodedSerializedTempData)

{

try {

return (new JavaScriptSerializer()).Deserialize<IDictionary<string, object>>(

HttpUtility.UrlDecode(base64EncodedSerializedTempData));

}

catch {

return null;

}

}

publicstaticstring 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中加一段以下配置就好了:

<sessionState mode="Custom" customProvider="CookieSessionStateStore">

<providers>

<add name="ProcCacheSessionStateStore" type="Fish.SampleCode.ProcCacheSessionStateStore"/>

<add name="CookieSessionStateStore" type="Fish.SampleCode.CookieSessionStateStore"/>

</providers>

</sessionState>

  好了,这次不用改代码了,在部署环境中,也只需要修改了一下配置就完事了。

  警告:我提供的这二个Provider只是做了简单的测试,并没经过实际的项目检验,如果您需要使用,请自行测试它的可用性。