24

我想将用户正在执行的某些操作的“状态”存储在一系列不同的 ASP.Net 网络表单中。我对持久状态的选择是什么,每种解决方案的优缺点是什么?

我一直在使用 Session 对象,并使用一些辅助方法来强类型化对象:

    public static Account GetCurrentAccount(HttpSessionState session)
    {
        return (Account)session[ACCOUNT];
    }

    public static void SetCurrentAccount(Account obj, HttpSessionState session)
    {
        session[ACCOUNT] = obj;
    }

许多消息来源告诉我“会话是邪恶的”,所以这确实是这个问题的根本原因。我想知道您认为“最佳实践”是什么,以及为什么。

4

13 回答 13

38

会话状态本身并没有什么坏处。

不过,有几件事情要记住,这可能会咬到你:

  1. 如果用户按下浏览器后退按钮,您将返回上一页,但您的会话状态不会恢复。因此,您的 CurrentAccount 可能不是页面上最初的样子。
  2. ASP.NET 进程可以被 IIS 回收。发生这种情况时,您的下一个请求将启动一个新进程。如果您正在使用进程会话状态,默认情况下,它将消失:-(
  3. 如果用户有一段时间不活动,会话也可能超时并产生相同的结果。这默认为 20 分钟,所以一顿美味的午餐就可以了。
  4. 使用进程外会话状态要求存储在会话状态中的所有对象都是可序列化的。
  5. 如果用户打开第二个浏览器窗口,他将期望拥有第二个不同的应用程序,但会话状态很可能会在两个之间共享。因此,在一个浏览器窗口中更改 CurrentAccount 将在另一个浏览器窗口中执行相同的操作。
于 2008-09-25T13:41:41.037 回答
19

临时存储表单数据的两种选择是,首先,将每个表单的信息存储在会话状态变量中,其次,使用 URL 参数传递表单信息。使用 Cookie 作为潜在的第三种选择根本不可行,原因很简单,您的许多访问者可能会关闭 cookie(但这不会影响会话 cookie)。另外,根据您的问题的性质,我假设您不希望在完全提交之前将此信息存储在数据库表中。

使用会话变量是这个问题的经典解决方案,但它确实存在一些缺点。其中包括 (1) 如果您使用 inproc 会话管理,大量数据可能会耗尽服务器 RAM,(2) 在服务器场中的多个服务器之间共享会话变量需要额外考虑,以及 (3) 专业设计的应用程序必须防止会话过期(不要只转换会话变量并使用它 - 如果会话已过期,则转换将引发错误)。然而,对于绝大多数应用程序来说,会话​​变量无疑是要走的路。

另一种方法是在 URL 中传递每个表单的信息。这种方法的主要问题是您必须非常小心地“传递”信息。例如,如果您在四个页面中收集信息,则需要在第一个页面中收集信息,将其在 URL 中传递到第二个页面,您必须将其存储在该页面的视图状态中。然后,当调用第三个页面时,您将从第二个页面收集表单数据以及视图状态变量,并在 URL 等中进行编码。如果您有五个或更多页面,或者如果访问者将在站点周围跳转,您你手上会一团糟。还要记住,所有信息都需要 A) 序列化为 URL 安全字符串 B) 以防止简单的基于 URL 的黑客攻击的方式编码(例如 如果您将价格以明文形式传递并传递,有人可能会更改价格)。请注意,您可以通过创建一种“会话管理器”并让它为您管理 URL 字符串来减少其中一些问题,但是您仍然必须对任何给定链接可能会破坏某人的整个会话的可能性非常敏感,如果它没有得到妥善管理。

最后,我只使用 URL 变量将非常有限的数据从一个页面传递到下一个页面(例如,在指向该项目的链接中编码的项目 ID)。

那么,让我们假设您确实会使用内置的会话功能来管理用户的数据。为什么有人会告诉你“会话是邪恶的”?好吧,除了上面提到的内存负载、服务器群和过期注意事项之外,对 Session 变量的主要批评是它们实际上是无类型变量。

幸运的是,谨慎使用 Session 变量可以避免内存问题(大项目无论如何都应该保存在数据库中),如果您运行的站点大到需要一个服务器场,那么有很多机制可用于共享 ASP 内置的状态.NET(提示:您不会使用 inproc 存储)。

为了从本质上避免 Session 的所有其他缺点,我建议实现一个对象来保存您的会话数据以及一些简单的 Session 对象管理功能。然后将它们构建到 Page 类的后代中,并将这个后代 Page 类用于所有页面。然后,通过页面类作为一组强类型值访问您的 Session 数据是一件简单的事情。请注意,您的对象字段将为您提供一种以强类型方式访问每个“会话变量”的方法(例如,每个变量一个字段)。

如果这对您来说是一项简单的任务,或者您是否想要一些示例代码,请告诉我!

于 2008-09-25T13:52:45.660 回答
10

据我所知,Session这是存储此信息的预期方式。请记住,会话状态通常默认存储在进程中。如果您有多个 Web 服务器,或者 IIS 重新启动,您将丢失会话状态。这可以通过使用 ASP.NET 状态服务,甚至是 SQL 数据库来存储会话来解决。这可以确保人们恢复他们的会话,即使他们被重新路由到不同的 Web 服务器,或者在工作进程回收的情况下。

于 2008-09-25T13:17:45.167 回答
6

至于“会话是邪恶的”......如果你在经典的 ASP 中开发,我不得不同意,但 ASP.NET/IIS 做得更好。

真正的问题是保持状态的最佳方式是什么。在我们的例子中,当涉及到当前登录的用户时,我们将该对象存储在 Session 中,因为我们不断地引用它来获取他们的姓名、电子邮件地址、授权等。

其他不需要任何长期持久性的小信息我们使用 cookie 和视图状态的组合

于 2008-09-25T13:17:19.800 回答
6

其声名狼藉的原因之一是匆忙的开发人员在 UI 代码中过度使用字符串文字(而不是像你这样的辅助类)作为项目键,并最终得到一大袋不可测试的混杂状态。某种包装器是非恶意会话使用的入门级要求。

于 2008-09-25T13:49:00.223 回答
5

当您想要在您的 Web 应用程序中存储可以全局访问的信息时,执行此操作的一种方法是ThreadStatic属性。这会将 a 的static成员Class变为由当前线程共享的成员,而不是由其他线程共享的成员。的优点ThreadStatic是您不必有可用的网络上下文。例如,如果您有一个不引用的后端System.Web,但也想在那里共享信息,您可以id在属性中的每个请求的开头设置用户ThreadStatic,并在您的依赖项中引用它而无需访问到Session对象。

因为它只static针对单个线程,所以我们确保其他同时访问者不会得到我们的会话。只要您确保为每个请求都重置该属性,这将有效。这使它成为饼干的理想伴侣。

于 2008-09-25T13:24:57.723 回答
3

我认为在这种情况下使用 Session 对象是可以的,但是您应该记住,如果长时间没有浏览器活动,Session 可能会过期(HttpSessionState.Timeout属性确定会话状态提供程序在多少分钟内终止会话),所以最好在返回之前检查值是否存在:

public static Account GetCurrentAccount(HttpSessionState session)
{
    if (Session[ACCOUNT]!=null)
        return (Account)Session[ACCOUNT];
    else
        throw new Exception("Can't get current account. Session expired.");
}
于 2008-09-25T13:36:18.540 回答
2

http://www.tigraine.at/2008/07/17/session-handling-in-aspnet/

希望这可以帮助。

于 2008-09-25T13:24:35.930 回答
1

短期信息,只需要生存到下一个请求,也可以存储在ViewState. 这意味着对象被序列化并存储在发送到浏览器的页面中,然后在单击事件或类似事件时将其发送回服务器。然后将ViewState其解码并再次变成对象,以备检索。

于 2008-09-25T13:21:14.627 回答
1

会话不是邪恶的,它们在 ASP.NET 应用程序中提供重要功能,提供在用户“会话”期间必须在多个页面之间共享的数据。有一些建议,我想说尽可能使用 SQL 会话管理,并确保您在会话集合中使用的对象是“可序列化的”。最佳实践是在绝对需要跨页面共享状态信息时使用会话对象,而在不需要时不要使用它。信息不会在客户端可用,会话密钥要么保存在 cookie 中,要么通过查询字符串保存,或者根据配置方式使用其他方法,然后会话对象在数据库表中可用(除非你使用 InProc,

于 2008-09-25T13:22:11.287 回答
1

Session as evil:不在 ASP.NET 中,正确配置。是的,尽可能地无国籍是理想的,但现实是你无法从这里到达那里。但是,您可以使 Session 以减轻其影响的方式运行——特别是 StateServer 或数据库会话。

于 2008-09-25T13:24:40.673 回答
1

我认为“邪恶”来自过度使用会话。如果你只是把任何东西都粘在里面(比如对所有东西都使用全局变量),你最终会表现不佳并且一团糟。

于 2008-09-25T13:37:29.283 回答
1

Anything you put in the session object stays there for the duration of the session unless it is cleaned up. Poor management of memory stored using inproc and stateserver will force you to scale out earlier than necessary. Store only an ID for the session/user in the session and load what is needed into the cache object on demand using a helper class. That way you can fine tune it's lifetime according to how often that data us used. The next version of asp.net may have a distributed cache(rumor).

于 2008-09-29T20:53:39.987 回答