在线时间:8:00-16:00
迪恩网络APP
随时随地掌握行业动态
扫描二维码
关注迪恩网络微信公众号
【IT168技术】本文将介绍Asp.net MVC 中的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只是做了简单的测试,并没经过实际的项目检验,如果您需要使用,请自行测试它的可用性。 |
请发表评论