13

最近,在为工作中的 ASP.NET 项目编写一些代码时。我们需要一个跟踪工具来获取用户活动的基本指标(页面点击计数等),我们将跟踪它们,然后通过inSession将数据保存到 DB 。Session_EndGlobal.asax

我开始破解,初始代码运行良好,在每个页面加载时更新数据库。我想在每个请求上删除这个数据库命中,只是依靠Session_End存储所有数据。

所有跟踪代码都封装在Tracker类中,包括本质上包装会话变量的属性。

问题 是当我Tracker.Log()Session_End方法中执行时HttpContext.Current.Session,Tracker 代码中的NullReferenceException. 现在,这是有道理的,因为HttpContext总是与当前请求相关,当然在 中Session_End,没有请求。

我知道它Global.asax有一个Session属性,它返回一个HttpSessionState实际上似乎工作正常的属性(我最终将它注入到跟踪器中)..

但我很好奇,我怎么能从外部获得对所HttpSessionState使用对象的相同引用?Global.asaxGlobal.asax

在此先感谢各位,感谢您的意见。:)

4

7 回答 7

18

为了更好地回答原始问题:

背景

每个页面请求都会启动一个新Session对象,然后从您的会话存储中扩充它。为此,它使用客户端提供的 cookie 或特殊路径构造(用于无 cookie 会话)。使用此会话标识符,它会查询会话存储并反序列化(这就是除了 InProc 之外的所有提供者都需要可序列化的原因)新的会话对象。

对于 InProc 提供程序,只需将它存储在HttpCache会话标识符键控中的引用交给您。这就是 InProc 提供程序在AppDomain回收时丢弃会话状态的原因(也是多个 Web 服务器无法共享 InProc 会话状态的原因。

这个新创建和膨胀的对象被卡在Context.Items集合中,因此它在请求期间可用。

然后,您对Session对象所做的任何更改都会在对会话存储的请求结束时通过序列化(或者在 InProc 的情况下,HttpCache条目被更新)进行持久化。

由于Session_End在没有当前请求的情况下立即触发,因此该Session对象在没有可用信息的情况下自动旋转。如果使用 InProc 会话状态,过期HttpCache会触发一个回调事件到您的Session_End事件中,因此会话条目可用,但仍然是上次存储在HttpContext.Cache. 该值HttpApplication.Session通过内部方法(称为ProcessSpecialRequest)针对属性存储,然后可用。在所有其他情况下,它在内部来自HttpContext.Current.Session值。

您的答案

由于 Session_End 总是针对空上下文触发,因此您应该始终在该事件中使用 this.Session 并将 HttpSessionState 对象传递给您的跟踪代码。在所有其他情况下,从跟踪代码中获取HttpContext.Current.Session然后传递给跟踪代码是非常好的。但是,不要让跟踪代码到达会话上下文

我的答案

Session_End除非您知道您正在使用的会话存储支持Session_End,否则不要使用,如果trueSetItemExpireCallback. 唯一的开箱即用商店是InProcSessionState商店。Session_End可以编写一个会话存储,但如果有多个服务器,谁将处理它的问题有点模棱两可。

于 2009-01-23T00:08:45.247 回答
11

Global.asax 实现 HttpApplication - 当您在其中调用它时,您正在与它交谈。

HttpApplication的 MSDN 文档详细介绍了如何在 HttpHandler 中获取它,然后访问它的各种属性。

然而

您的应用程序可以创建多个 HttpApplication 实例来处理并行请求,并且这些实例可以重复使用,因此仅以某种方式选择它并不能保证您拥有正确的实例。

我还要注意一点 - 如果您的应用程序崩溃,则无法保证 session_end 将被调用,并且您将丢失所有会话中的所有数据,这显然不是一件好事。

我同意在每个页面上记录可能不是一个好主意,但可能是发生一些异步日志记录的中途之家 - 你将细节发送到日志记录类,不时记录你所追求的细节 - 仍然不是 100%如果应用程序崩溃,它是可靠的,但你不太可能失去一切。

于 2009-01-21T11:49:43.210 回答
4

我想你已经回答了你自己的问题:通常 Global.asax 和 HttpContext.Current.Session 中的 Session 属性是相同的(如果有当前请求)。但是在会话超时的情况下,没有活动请求,因此您不能使用 HttpContext.Current。

如果要从 Session_End 调用的方法访问会话,则将其作为参数传递。创建一个重载版本的 Log() 方法,该方法将 HttpSessionState 作为参数,然后从 Session_End 事件处理程序调用 Tracker.Log(this.Session)。

BTW:你知道在任何情况下你都不能依赖会话结束事件吗?只要您有正在进行的会话状态,它就会起作用。使用 SQL server 或 StateServer 管理会话状态时,不会触发会话结束事件。

于 2009-01-21T08:49:51.547 回答
3

只有在文件中设置为Session_End时才会引发该事件。如果会话模式设置为or ,则不会引发事件。 sessionstate modeInProcWeb.configStateServerSQLServer

用于Session["SessionItemKey"]获取会话值。

于 2011-01-21T07:53:27.133 回答
0

好的,我在跟踪会话活动时遇到了同样的问题。我没有使用 session_end 事件,而是为我的 sessiontracker 类实现了 IDisposable 接口和析构函数。我已修改 Dispose() 方法以将会话活动保存到数据库。当用户单击注销按钮时,我调用了方法 obj.Dispose()。如果用户错误地关闭了浏览器,那么 GC 将在清理对象时调用析构函数(不是立即,但肯定会在一段时间后调用此方法)。析构函数方法在内部执行相同的 Dispose() 方法以将会话活动保存到 DB 中。

-山

于 2009-07-16T15:31:36.070 回答
0

在 Session_Start 事件期间,您的 Global.asax 文件中提供了会话。也许等到这一点做的事情?

于 2011-11-09T10:28:31.920 回答
-1

请记住 Session_End 在会话超时而没有活动时运行。浏览器不会发起该事件(因为它处于非活动状态),因此您真正获得该事件的唯一时间是使用 InProc 提供程序时。在每个其他提供者中,此事件将永远不会触发。

道德?不要使用 Session_End。

于 2009-01-21T08:51:40.247 回答