最近,在为工作的ASP.NET项目处理一些代码时。我们需要一个跟踪工具来获取用户活动(页面点击次数等)的基本指标,我们会在Session
中跟踪它们,然后通过Global.asax
中的Session_End
将数据保存到数据库。
我开始乱砍,初始代码工作正常,在每个页面加载时更新数据库。我想在每个请求中删除此数据库命中,只需依靠Session_End
来存储所有数据。
所有跟踪代码都封装在Tracker
类中,包括基本上包装Session变量的属性。
问题是当我在Tracker.Log()
方法中执行Session_End
时,跟踪器代码中的HttpContext.Current.Session
失败并显示NullReferenceException
。现在,这是有道理的,因为HttpContext
始终与当前请求相关,当然在Session_End
中,没有请求。
我知道Global.asax
有一个Session
属性,返回HttpSessionState
实际上似乎工作正常(我最终将其注入跟踪器)..
但我很好奇,我怎么能在HttpSessionState
的外面 Global.asax
Global.asax
使用的{{1}}对象获得相同的引用吗
先谢谢你们,我很感激投入。 :)
答案 0 :(得分:16)
更好地回答原始问题:
每个页面请求都会旋转一个新的Session
对象,然后将其从会话存储中膨胀。为此,它使用客户端提供的cookie或特殊路径构造(用于无cookie会话)。使用此会话标识符,它会查询会话存储并反序列化(这就是为什么所有提供程序,但InProc必须是可序列化的)新会话对象。
对于InProc提供者,只需将您存储在会话标识符键入的HttpCache
中的引用交给您。 这就是为什么InProc提供程序在AppDomain
被回收时会丢弃会话状态(以及为什么多个Web服务器无法共享InProc会话状态。
这个新创建和膨胀的对象卡在Context.Items
集合中,以便在请求期间可用。
您对Session
对象所做的任何更改都会在请求结束时通过序列化(或InProc的情况,HttpCache
条目更新)保留。
由于Session_End
在没有当前请求的情况下触发,因此Session
对象在ex-nilo中旋转,没有可用信息。如果使用InProc会话状态,HttpCache
的到期会将回调事件触发到您的Session_End
事件中,因此会话条目可用,但仍然是最后存储在{{1}中的内容的副本}}。此值通过内部方法(称为HttpContext.Cache
)针对HttpApplication.Session
属性存储,然后可用。在所有其他情况下,它内部来自ProcessSpecialRequest
值。
由于Session_End总是针对空上下文触发,因此您应该始终在该事件中使用this.Session并将HttpSessionState对象传递给您的跟踪代码。在所有其他上下文中,从HttpContext.Current.Session
获取然后传递到跟踪代码是完全正常的。但是,不要,让跟踪代码到达会话上下文。
不要使用HttpContext.Current.Session
,除非您知道您正在使用的会话商店支持Session_End
,如果它从{{返回Session_End
1}}。唯一的内置商店是true
商店。可以编写一个会话存储,但如果有多个服务器,那么处理SetItemExpireCallback
的人的问题会有些模糊。
答案 1 :(得分:10)
Global.asax实现了HttpApplication - 当你从中调用 this 时,你正在谈论它。
HttpApplication的MSDN文档详细介绍了如何在HttpHandler中获取它,然后可以访问它上面的各种属性。
<强>无论其强>
您的应用程序可以创建多个HttpApplication实例来处理并行请求,这些实例可以重复使用,所以只是以某种方式提取它并不能保证您拥有正确的实例。
我也会添加注意事项 - 如果您的应用程序崩溃,则不会保证调用session_end,并且您将丢失所有会话中的所有数据,显然不是一件好事。
我同意登录每个页面可能不是一个好主意,但也许是一个中途发生异步日志记录的房子 - 你将详细信息发送到日志记录类,然后每隔一段时间记录你所追踪的细节 - 仍然如果应用程序崩溃,则不是100%稳固,但你不太可能失去一切。
答案 2 :(得分:3)
我认为你已经回答了自己的问题:通常Global.asax和HttpContext.Current.Session中的Session属性是相同的(如果有当前请求)。但是在会话超时的情况下,没有活动请求,因此您不能使用HttpContext.Current。
如果要从Session_End调用的方法访问会话,请将其作为参数传递。创建一个重载版本的Log()方法,它将HttpSessionState作为参数,然后从Session_End事件处理程序调用Tracker.Log(this.Session)。
BTW:您知道在任何情况下都不能依赖会话结束事件吗?只有在进程中具有会话状态时,它才会起作用。使用SQL Server或StateServer管理会话状态时,会话结束事件不会触发。答案 3 :(得分:2)
仅当Session_End
文件中sessionstate mode
设置为InProc
时才会引发Web.config
事件。如果会话模式设置为StateServer
或SQLServer
,则不会引发该事件。
使用Session["SessionItemKey"]
获取会话值。
答案 4 :(得分:0)
好的,跟踪会话活动存在同样的问题。我没有使用session_end事件,而是在我的sessiontracker类中实现了IDisposable接口和析构函数。我修改了Dispose()方法以将会话活动保存到DB。当用户单击注销按钮时,我调用了方法obj.Dispose()。如果用户错误地关闭了浏览器,那么GC会在清理对象时调用析构函数(不是立即,但肯定会在一段时间之后调用此方法)。析构函数方法在内部执行相同的Dispose()方法,以将会话活动保存到DB中。
-Shan
答案 5 :(得分:0)
会话在Global.asax文件中可用。也许等到这一点做事情?
答案 6 :(得分:-1)
请记住,当会话超时而没有活动时,Session_End会运行。浏览器不会发起该事件(因为它处于非活动状态),因此您实际获得该事件的唯一时间是使用InProc提供程序。在其他提供商中,此活动永远不会开始。
道德?不要使用Session_End。