如何在ASP.NET MVC 4应用程序中使用会话?

123

我是ASP.NET MVC的新手。之前我使用过PHP,创建会话并基于当前会话变量选择用户记录非常容易。

我在互联网上寻找了许多简单的、逐步指导的教程,以便向我展示如何在我的C# ASP.NET MVC 4应用程序中创建和使用会话。我想要创建一个会话,其中包含用户变量,我可以从任何控制器中访问这些变量,并能在我的LINQ查询中使用这些变量。


2
可能与如何使用ASP.NET MVC维护用户会话重复。 - KV Prajapati
2
这篇文章可能会引起您的兴趣:http://brockallen.com/2012/04/07/think-twice-about-using-session-state/ - Daniel Hollinrake
5个回答

170

尝试

//adding data to session
//assuming the method below will return list of Products

var products=Db.GetProducts();

//Store the products to a session

Session["products"]=products;

//To get what you have stored to a session

var products=Session["products"] as List<Product>;

//to clear the session value

Session["products"]=null;

3
谢谢,Jobert!你给了我一个想法!但我在想...在登录期间是否可以向会话添加用户变量?并且,我是否可以跨应用程序的不同控制器访问仅在创建时存在的会话变量? - Thuto Paul Gaotingwe
32
您可以将任何类型的数据存储在会话中。创建后,您可以跨所有视图和控制器访问存储在其中的值。请注意,每个用户和每个浏览器只能访问其创建的会话。这意味着使用Firefox创建的User1会话不能由同一用户使用IE访问。还有一些不应使用会话的事项,比如不要将大型数据存储在其中,否则可能会降低服务器性能。最后,不要将敏感数据存储到会话中,例如密码或信用卡号。 - Jobert Enamno
2
再次感谢!我应该将它创建在哪里以便能够在不同的控制器中访问? - Thuto Paul Gaotingwe
2
@JobertEnamno,将来自WebSecurity.CurrentUserId的值存储起来以避免多次从数据库中获取它是否安全(我发现这样做非常昂贵)? - Andrius Naruševičius
2
没有跨控制器会话,因此当您从Account/LogOn请求另一个控制器(例如到Home/Index)时,Session["FirstName"]null。 开发人员必须创建一个父控制器(BaseController),并定义一个受保护的字段(internal protected HttpSessionStateBase SharedSession),该字段可以在所有子控制器中公开共享的会话变量(假设您的所有应用程序控制器都继承自BaseController)。 - Bellash
显示剩余4条评论

68
由于Web的无状态特性,会话也是通过将对象序列化并将其存储在会话中来持久化跨请求的极其有用的方式。
这个完美的使用案例可能是,如果您需要在应用程序中访问常规信息,以便在每个请求上保存额外的数据库调用,那么这些数据可以存储在一个对象中,并在每个请求上反序列化,如下所示:
我们的可重用、可序列化对象:
[Serializable]
public class UserProfileSessionData
{
    public int UserId { get; set; }

    public string EmailAddress { get; set; }

    public string FullName { get; set; }
}

使用案例:

public class LoginController : Controller {

    [HttpPost]
    public ActionResult Login(LoginModel model)
    {
        if (ModelState.IsValid)
        {
            var profileData = new UserProfileSessionData {
                UserId = model.UserId,
                EmailAddress = model.EmailAddress,
                FullName = model.FullName
            }

            this.Session["UserProfile"] = profileData;
        }
    }

    public ActionResult LoggedInStatusMessage()
    {
        var profileData = this.Session["UserProfile"] as UserProfileSessionData;

        /* From here you could output profileData.FullName to a view and
        save yourself unnecessary database calls */
    }

}

一旦这个对象被序列化,我们就可以在所有控制器中使用它,而不需要再次创建它或查询其中包含的数据。

使用依赖注入注入您的会话对象

在理想情况下,您应该“针对接口编程,而不是实现”,并使用您选择的反转控制容器将可序列化的会话对象注入到您的控制器中,例如(此示例使用StructureMap,因为我最熟悉它)。

public class WebsiteRegistry : Registry
{
    public WebsiteRegistry()
    {
        this.For<IUserProfileSessionData>().HybridHttpOrThreadLocalScoped().Use(() => GetUserProfileFromSession());   
    }

    public static IUserProfileSessionData GetUserProfileFromSession()
    {
        var session = HttpContext.Current.Session;
        if (session["UserProfile"] != null)
        {
            return session["UserProfile"] as IUserProfileSessionData;
        }

        /* Create new empty session object */
        session["UserProfile"] = new UserProfileSessionData();

        return session["UserProfile"] as IUserProfileSessionData;
    }
}

你需要在Global.asax.cs文件中注册它。
对于那些不熟悉注入会话对象的人,您可以在这里找到更深入的博客文章。 一个警告: 值得注意的是,应将会话保持最小化,大型会话可能会导致性能问题。
还建议不要在其中存储任何敏感数据(密码等)。

你会把类定义放在哪里呢?我还是个新手,但我只是好奇其他控制器如何看到这个类并知道它是什么。你是把它添加到控制器的顶部吗?我想在global.asax的SessionStart中初始化一些东西,但也许这不是最好的方法。 - Shaun314
@Shaun314 理想情况下,您应该使用IoC容器通过依赖注入将对象注入到控制器中(请参见编辑)。 - Joseph Woodward
1
我在使用Identity 2登录用户后存储一些会话信息。除了第一个重定向用户的操作和控制器之外,我无法在其他操作和控制器中检索这些信息。有什么想法吗? - Akbari

18

这是ASP.NET和ASP.NET MVC中会话状态的工作方式:

ASP.NET Session State概述

基本上,你可以使用以下方法将值存储在Session对象中:

Session["FirstName"] = FirstNameTextBox.Text;

获取数值的方法:

var firstName = Session["FirstName"];

10
没有跨控制器会话,因此当您从一个控制器(例如从“账户”到“主页”)请求另一个控制器时,Session["FirstName"]为空。开发人员需要创建一个BaseController,并定义一个受保护的字段(internal protected HttpSessionStateBase SharedSession),该字段可以在所有子控制器中公开共享的Session变量(这假设您所有的应用程序控制器都继承自BaseController)。 - Bellash
6
嗯,当然有啊。在控制器中(MVC提供的基础控制器)有一个会话变量。 - aeliusd
8
@Bellash 这完全是错的。会话可以跨控制器使用,我刚在HomeController中设置了Session["test"],然后在AccountController中读取它。 - niico

0

您可以使用以下方法在会话中存储任何类型的数据:

Session["VariableName"]=value;

这个变量将持续大约20分钟。


-8

您可以将任何值存储在会话中,例如 Session["FirstName"] = FirstNameTextBox.Text; 但我建议您将其作为模型中的静态字段,并为其分配值,这样您就可以在应用程序的任何地方访问该字段的值。您不需要使用会话。应避免使用会话。

public class Employee
{
   public int UserId { get; set; }
   public string EmailAddress { get; set; }
   public static string FullName { get; set; }
}

在控制器上 - Employee.FullName = "ABC"; 现在您可以在应用程序的任何地方访问这个全名。


10
在多用户环境中,将数据存储在静态字段上,特别是像员工姓名这样的用户数据,会导致严重的问题。当两个不同的用户登录系统时,由于Employee类上的静态字段对于每个实例都是相同的,他们将看到相同的员工电子邮件地址。 - Gökçer Gökdal

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接