“HttpContext.Current.Session”vs Global.asax“this.Session”

时间:2009-01-21 08:37:23

标签: asp.net session session-state global-asax

最近,在为工作的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}}对象获得相同的引用吗

先谢谢你们,我很感激投入。 :)

7 个答案:

答案 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事件。如果会话模式设置为StateServerSQLServer,则不会引发该事件。

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

答案 4 :(得分:0)

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

-Shan

答案 5 :(得分:0)

在Session_Start事件期间,

会话在Global.asax文件中可用。也许等到这一点做事情?

答案 6 :(得分:-1)

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

道德?不要使用Session_End。