ASP.NET MVC中的会话变量


169

我正在编写一个Web应用程序,它将允许用户浏览网站中发出某些请求的多个网页。用户输入的所有信息都将存储在我创建的对象中。问题是我需要从网站的任何部分访问此对象,而我真的不知道实现此目的的最佳方法。我知道一种解决方案是使用会话变量,但是我不知道如何在asp .net MVC中使用它们。在哪里可以声明会话变量?还有其他办法吗?


3
您正在混合使用Web站点和Web应用程序的概念……它们不是同一回事。
adripanico 2012年

1
听起来像需要数据库
Coops 2014年

Answers:


123

我想您会想想事情是否真的属于会话状态。这是我发现自己时常会做的事情,并且是对整个事情的一种很好的强类型化方法,但是在将事情放在会话上下文中时应该小心。并非所有内容都应该存在,仅因为它属于某个用户。

在global.asax中钩上OnSessionStart事件

void OnSessionStart(...)
{
    HttpContext.Current.Session.Add("__MySessionObject", new MySessionObject());
}

从代码中HttpContext.Current属性!= null的任何地方,您都可以检索该对象。我用扩展方法来做到这一点。

public static MySessionObject GetMySessionObject(this HttpContext current)
{
    return current != null ? (MySessionObject)current.Session["__MySessionObject"] : null;
}

这样你可以在代码中

void OnLoad(...)
{
    var sessionObj = HttpContext.Current.GetMySessionObject();
    // do something with 'sessionObj'
}

32
如果正在使用ASP MVC,则最好不要使用HttpContext.Current.Session中的实际Session对象,而是使用System.Web.Abstractions.dll中新的HttpSessionStateWrapper&HttpSessionStateBase,然后使用Factory或DI来获取Session。
Paul

6
如何为会话变量分配内容?(而不是仅访问)
raklos 2011年

31
人们试图搞清楚“OnSessionStart”事件是什么,以及如何你“勾”了,看到stackoverflow.com/questions/1531125/...
Cephron

5
@Paul您能提供一个例子吗?我似乎找不到使用HttpSessionStateWrapper的任何示例。
约瑟夫·伍德沃德2013年

4
@AjayKelkar这个评论线程建议“如果正在使用ASP MVC,那么最好不要使用来自HttpContext.Current.Session的实际Session对象,而要使用新的HttpSessionStateWrapper&HttpSessionStateBase”,这建议您的答案不会更好
Coops,2014年

48

答案是正确的,但是我很难在ASP.NET MVC 3应用程序中实现它。我想访问控制器中的Session对象,却无法弄清楚为什么我一直在获取“ Instance not set to Object error的实例”。我注意到的是,在控制器中,当我尝试通过执行以下操作来访问会话时,我不断收到该错误。这是由于this.HttpContext是Controller对象的一部分。

this.Session["blah"]
// or
this.HttpContext.Session["blah"]

但是,我想要的是HttpContext,它是System.Web命名空间的一部分,因为上面的答案建议在Global.asax.cs中使用它。因此,我必须明确地执行以下操作:

System.Web.HttpContext.Current.Session["blah"]

这对我有所帮助,不确定我是否在这里做过不是MO的事情,但我希望它对某人有帮助!


6
System.Web.HttpContext.Current.Session [“ blah”] =值
Tomasz Iniewicz 2012年

21

因为我不喜欢在该位置看到“ HTTPContext.Current.Session”,所以我使用单例模式来访问会话变量,因此可以轻松访问强类型的数据包。

[Serializable]
public sealed class SessionSingleton
{
    #region Singleton

    private const string SESSION_SINGLETON_NAME = "Singleton_502E69E5-668B-E011-951F-00155DF26207";

    private SessionSingleton()
    {

    }

    public static SessionSingleton Current
    {
        get
        {
            if ( HttpContext.Current.Session[SESSION_SINGLETON_NAME] == null )
            {
                HttpContext.Current.Session[SESSION_SINGLETON_NAME] = new SessionSingleton();
            }

            return HttpContext.Current.Session[SESSION_SINGLETON_NAME] as SessionSingleton;
        }
    }

    #endregion

    public string SessionVariable { get; set; }
    public string SessionVariable2 { get; set; }

    // ...

那么您可以从任何地方访问数据:

SessionSingleton.Current.SessionVariable = "Hello, World!";

2
因此,此类有两个职责:维护一个实例,并存储变量...我将使用IOC容器来实现单例。
Jowen

1
如果您已经有了一个设置,那么我可能也会提供一个成熟的可注入会话服务,但是一致性可能是最大的优势,如果您愿意的话,我更倾向于将此代码用于小型功能集Web应用程序。
Dead.Rabit 2013年

14

如果您使用的是asp.net mvc,这是访问会话的一种简单方法。

从控制器:

{Controller}.ControllerContext.HttpContext.Session["{name}"]

从视图:

<%=Session["{name}"] %>

绝对不是访问会话变量的最佳方法,但这是直接途径。因此,请谨慎使用(最好在快速原型制作期间使用),并在合适时使用Wrapper / Container和OnSessionStart。

高温超导


2
嗯..哪种方法最好?我应该将数据从控制器上的Session传递到ViewState,不是吗?
RredCat 2011年

2
您能解释一下这种方法的局限性吗?
RredCat 2011年

1
我认为他的意思是最好拥有读/写方法。根据并发/线程的使用情况,您可能还需要在那些读/写方法中锁定,以避免出现竞争状况。
DeepSpace101 '02

13

好吧,恕我直言

  1. 永远不要在视图/母版页中引用会话
  2. 尽量减少您对Session的使用。MVC为此提供了TempData obj,它基本上是一个Session,仅用于一次服务器访问。

关于#1,我有一个强类型的主视图,该属性具有访问Session对象所代表的内容的属性....在我的实例中,强类型的主视图是通用的,这使我对强类型的视图页面具有一定的灵活性

ViewMasterPage<AdminViewModel>

AdminViewModel
{
    SomeImportantObjectThatWasInSession ImportantObject
}

AdminViewModel<TModel> : AdminViewModel where TModel : class
{
   TModel Content
}

然后...

ViewPage<AdminViewModel<U>>

7

虽然我不了解asp.net mvc,但这是我们在常规.net网站中应该做的。它也应适用于asp.net mvc。

YourSessionClass obj=Session["key"] as YourSessionClass;
if(obj==null){
obj=new YourSessionClass();
Session["key"]=obj;
}

您可以将其放在易于访问的方法中。高温超导



7

我访问会话的方式是编写一个帮助程序类,其中封装了各种字段名称及其类型。我希望这个例子有帮助:

using System;
using System.Collections.Generic;
using System.Web;
using System.Web.SessionState;

namespace dmkp
{
    /// <summary>
    /// Encapsulates the session state
    /// </summary>
    public sealed class LoginInfo
    {
        private HttpSessionState _session;
        public LoginInfo(HttpSessionState session)
        {
            this._session = session;
        }

        public string Username
        {
            get { return (this._session["Username"] ?? string.Empty).ToString(); }
            set { this._session["Username"] = value; }
        }

        public string FullName
        {
            get { return (this._session["FullName"] ?? string.Empty).ToString(); }
            set { this._session["FullName"] = value; }
        }
        public int ID
        {
            get { return Convert.ToInt32((this._session["UID"] ?? -1)); }
            set { this._session["UID"] = value; }
        }

        public UserAccess AccessLevel
        {
            get { return (UserAccess)(this._session["AccessLevel"]); }
            set { this._session["AccessLevel"] = value; }
        }

    }
}

我喜欢您的答案...您能否进一步说明正在发生的事情...以及为什么这是与该线程上其他答案相对的更好的方法。
Chef_Code

6

伙计们给出了很好的答案,但我提醒您不要一直依赖会议。这样做很容易快捷,当然可以,但是在所有情况下都不会很好。

例如,如果您遇到主机托管不允许使用会话的情况,或者您在Web场中,或者在共享SharePoint应用程序的示例中。

如果您想使用其他解决方案,可以考虑使用IOC容器(例如Castle Windsor),创建一个提供程序类作为包装器,然后根据您的要求使用每种请求或会话方式保留类的一个实例。

IOC将确保每次都返回相同的实例。

更为复杂的是,如果您需要一个简单的解决方案,请使用会话。

下面是一些有趣的实现示例。

使用此方法,您可以按照以下方式创建提供程序类:

public class CustomClassProvider : ICustomClassProvider
{
    public CustomClassProvider(CustomClass customClass)
    { 
        CustomClass = customClass;
    }

    public string CustomClass { get; private set; }
}

并注册如下内容:

public void Install(IWindsorContainer container, IConfigurationStore store)
{
    container.Register(
            Component.For<ICustomClassProvider>().UsingFactoryMethod(
                () => new CustomClassProvider(new CustomClass())).LifestylePerWebRequest());
    }

4

您可以将ViewModelBase用作所有模型的基类,此类将负责从会话中提取数据

class ViewModelBase 
{
  public User CurrentUser 
  {
     get { return System.Web.HttpContext.Current.Session["user"] as User };
     set 
     {
        System.Web.HttpContext.Current.Session["user"]=value; 
     }
  }
}

您可以在HttpContextBase上编写一个扩展方法来处理会话数据

T FromSession<T>(this HttpContextBase context ,string key,Action<T> getFromSource=null) 
{
    if(context.Session[key]!=null) 
    {
        return (T) context.Session[key];
    }
  else if(getFromSource!=null) 
  {
    var value = getFromSource();
   context.Session[key]=value; 
   return value; 
   }
  else 
  return null;
}

在控制器中像下面这样使用

User userData = HttpContext.FromSession<User>("userdata",()=> { return user object from service/db  }); 

第二个参数是可选的,当会话中不存在值时,它将用于填充该键的会话数据。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.