ASP.NET会话状态/ NInject/OnePerRequest行为的问题

Mic*_*ith 5 asp.net lifecycle redirect ninject session-state

这是一个很长的帖子,所以请耐心等待.我不确定它主要是关于ASP.NET会话状态行为,NInject,应用程序设计还是重构.继续阅读,然后你可以决定...... :-)

背景

首先,有点背景.我们正在努力将大型网上商店重构为更易于维护的结构化设计.网上商店目前在.NET 3.5上运行,但设计更像是传统ASP时代的宿醉.显然,我们无法一次性解决所有问题,因此许多功能/技术/方法必须作为一个给定的.考虑到这一点...

该应用程序在上下文对象中维护与当前会话(用户配置文件,购物车,会话选择等)有关的所有内容,该上下文对象只是一个大型XML文档,可以作为字符串从Session中序列化和反序列化.XML格式也很重要,因为渲染是通过XSLT完成的.

这导致了许多问题:

  1. 这是一种关注太多的上帝对象.
  2. 它是松散类型的,并且过分依赖于XML操作/ XPath.
  3. 没有标准的方法/模式来检索会话xml文档或将其写回.我们有一个可怕的混合方法,将文档作为参数,修改并返回它,自己检索它的方法,修改它并将其保存回会话等等.这导致了很多困难.跟踪错误,过度使用会话中的序列化/反序列化等.

我们的方案

我们所做的是尝试在xml文档周围引入一个强类型包装器,它将其分解为不同的关注点,并透明地管理应用程序的其余部分的生命周期.
我们的目标是以下工作流程:

  • 在请求开始时,我们从会话中存储的xml字符串填充会话文档.

  • 应用程序的其余部分仅通过强类型包装器与其进行交互.整个应用程序使用相同的实例,不必担心何时检索或将状态保存回会话.

  • 在请求结束时,基础xml文档被序列化回Session.

由于我们使用NInject(v1)作为首选IOC,因此我们决定使用它来管理上下文对象的生命周期.上下文对象用OnePerRequest属性包装,dispose方法连接到一个方法,该方法将xml文档作为字符串保存回Session.

它不起作用......

我们很快遇到了NInject OnePerRequest模块似乎无法访问SessionState的问题.我们尝试的第一件事是黑客,我们将Session对象保存在变量中,以确保我们仍然可以写入它.这似乎适用于开发机器,但很明显它在移动到进程状态时没有.

它仍然不起作用......

我们尝试从OnePerRequest行为/模块继承,并添加IRequiresSessionState标记接口(OnePerRequestRequiresSessionState).但是,这还不够,因为NInject用于释放引用和清理的方法被连接到EndRequest方法.会话在EndRequest中可用,但它已经被序列化到进程外状态服务器,因此当在下一个请求开始时检索会话字符串时,不会反映现在更改的内容.然后我们决定改变偶数t来连接.我们抛弃了EndRequest并将我们的OnePerRequestRequiresSessionState"release all"方法连接到PostRequestHandlerExecute事件,该事件是在会话数据被序列化到进程之前.

它有效...然后它不......

这似乎有效.在单个服务器和Web场上.然后我们注意到奇怪的行为.似乎有两个不同版本的上下文,你会在它们之间随机切换.添加一些东西到购物车,它不存在.转到浏览其他产品,之前的产品将显示在购物车中.

经过一些追踪,我们发现了罪魁祸首:Response.Redirect.在整个网站上洒满了数百个地方的是Response.Redirect(url);. 使用此版本的重定向,将立即停止执行页面.这意味着不会触发PostRequestHandlerExecute,并且NInject不会抛弃当前版本的Context对象......一切都会崩溃.没有正确创建新版本等.EndRequest被解雇,这就是普通NInject OnePerRequest模块正常工作的原因,而不是我们试图使用会话状态的标准化版本.

当然,还有一个覆盖Response.Redirect,您可以在其中传递一个布尔值,告诉它是否终止现有页面或继续执行 - Response.Redirect(url,false).继续显然会触发我们的事件,一切正常但是......它继续执行页面的其余部分!这意味着执行调用Redirect之后的所有内容,我们完全不知道这意味着什么(因为现有网站希望它停止).

接下来是什么?

那么,有什么建议吗?到目前为止,我们已经讨论过:

  1. 抽象我们的重定向行为并通过一个控制重定向的中央方法(可能是一种方法来调用PostRequestHandlerExecute甚至可能是我们的NInject模块也可以订阅和清理的自定义Redirect事件).
  2. 看看是否有办法我们可以强制Session对象保存在EndRequest中,如果它先前没有保存在PostRequestHandlerExecute中,并在EndRequest中执行ninject清理
  3. 完全删除我们对Session的依赖并使用另一种存储机制:DB,文档DB,分布式HashTable等.有什么建议吗?我们没有想到的建议?你尝试过的东西有没有用过?

dav*_*ben 2

我认为你走在正确的道路上。这是我的一些想法:

  • 除了您拥有的强类型包装器之外,我还建议使用一个外观来访问返回包装器的上下文对象,例如 IContextProvider。这样您就可以逐步引入它,然后当它完全集成时,您可以重构提供程序而不会破坏使用它的东西。我不知道,但你可能已经这样做了。如果您选择的话,更改持久性机制也会更容易。如果你能做到这一点,我建议一旦你将所有依赖项与上下文对象隔离,将其更改为不以 XML 形式持久存在。SessionState 将更快地存储二进制对象,并且如果需要进行转换,您始终可以序列化为 XML。

  • 我认为 Ninject 不是您想要做的事情的正确机制。在 Ninject 中很难发出请求结束的信号,因为不能依赖垃圾收集。您是否考虑过使用 IHttpModule 来代替?您可以使用 AcquireRequestState 和 ReleaseRequestState 或 EndRequest 来处理获取/设置会话中的上下文。只允许应用程序通过门面获取上下文对象。

  • 如果您在网络农场上,那么您可能正在使用数据库来存储会话,因此将您的上下文放入数据库不会有太大不同。