Variables de sesión en ASP.NET MVC

169

Estoy escribiendo una aplicación web que permitirá a un usuario navegar por varias páginas web dentro del sitio web haciendo ciertas solicitudes. Toda la información que ingrese el usuario se almacenará en un objeto que creé. El problema es que necesito acceder a este objeto desde cualquier parte del sitio web y realmente no sé la mejor manera de lograrlo. Sé que una solución es usar variables de sesión, pero no sé cómo usarlas en asp .net MVC. ¿Y dónde declararía una variable de sesión? ¿Hay alguna otra manera?

Draco
fuente
3
Está mezclando los conceptos de sitio web y aplicación web ... no son lo mismo.
adripanico
1
Parece una necesidad de una base de datos
Coops
1
Posible duplicado de Cómo usar sesiones en una aplicación ASP.NET MVC 4?
Michael Freidgeim

Respuestas:

123

Creo que querrás pensar si las cosas realmente pertenecen a un estado de sesión. Esto es algo que me encuentro haciendo de vez en cuando y es un buen enfoque fuertemente tipado de todo, pero debes tener cuidado al poner las cosas en el contexto de la sesión. No todo debería estar allí solo porque pertenece a algún usuario.

en global.asax conecta el evento OnSessionStart

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

Desde cualquier parte del código donde la propiedad HttpContext.Current! = Null puede recuperar ese objeto. Hago esto con un método de extensión.

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

De esta manera puedes en código

void OnLoad(...)
{
    var sessionObj = HttpContext.Current.GetMySessionObject();
    // do something with 'sessionObj'
}
John Leidegren
fuente
32
Si se está utilizando ASP MVC, es preferible no usar el objeto Session real de HttpContext.Current.Session, sino usar el nuevo HttpSessionStateWrapper & HttpSessionStateBase de System.Web.Abstraction.dll y luego usar Factory o DI para obtener la sesión.
Paul
66
¿Cómo se asigna algo a la variable de sesión? (en lugar de solo acceder)
raklos
31
Para las personas que intentan descubrir qué es el evento "OnSessionStart" y cómo lo "enganchan", vea stackoverflow.com/questions/1531125/…
Cephron
55
@Paul ¿Puedes dar un ejemplo? Parece que no puedo encontrar ningún ejemplo de uso de HttpSessionStateWrapper.
Joseph Woodward
44
@AjayKelkar Este hilo de comentarios sugirió "Si se está utilizando ASP MVC, entonces es preferible no usar el objeto Session real de HttpContext.Current.Session sino usar el nuevo HttpSessionStateWrapper & HttpSessionStateBase", que sugiere que sus respuestas no son mejores
Coops
48

La respuesta aquí es correcta, sin embargo, tuve problemas para implementarlo en una aplicación ASP.NET MVC 3. Quería acceder a un objeto de sesión en un controlador y no podía entender por qué seguía obteniendo una "Instancia no establecida en una instancia de un error de Objeto". Lo que noté es que en un controlador cuando intenté acceder a la sesión haciendo lo siguiente, seguí recibiendo ese error. Esto se debe al hecho de que this.HttpContext es parte del objeto Controller.

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

Sin embargo, lo que quería era el HttpContext que es parte del espacio de nombres System.Web porque este es el que la respuesta anterior sugiere usar en Global.asax.cs. Así que tuve que hacer explícitamente lo siguiente:

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

esto me ayudó, no estoy seguro si hice algo que no sea MO por aquí, ¡pero espero que ayude a alguien!

Tomasz Iniewicz
fuente
66
System.Web.HttpContext.Current.Session ["blah"] = value
Tomasz Iniewicz
21

Debido a que no me gusta ver "HTTPContext.Current.Session" sobre el lugar, utilizo un patrón singleton para acceder a las variables de sesión, le brinda una bolsa de datos fuertemente tipada fácil de acceder.

[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; }

    // ...

entonces puede acceder a sus datos desde cualquier lugar:

SessionSingleton.Current.SessionVariable = "Hello, World!";
Dead.Rabit
fuente
2
Entonces, esta clase tiene dos responsabilidades: mantener una sola instancia y almacenar variables ... Yo usaría un contenedor IOC para tener un singleton.
Jowen
1
Si ya tiene una configuración, probablemente también haría un servicio de sesión inyectable totalmente desarrollado, aunque las consistencias probablemente sean la mayor ventaja, estaría más inclinado a usar este código para pequeñas aplicaciones web de conjunto de características ... webwizards si lo desea.
Dead.Rabit
14

Si está utilizando asp.net mvc, aquí hay una forma sencilla de acceder a la sesión.

De un controlador:

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

Desde una vista:

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

Definitivamente, esta no es la mejor manera de acceder a las variables de sesión, pero es una ruta directa. Por lo tanto, úselo con precaución (preferiblemente durante la creación rápida de prototipos) y use un Contenedor / Contenedor y OnSessionStart cuando sea apropiado.

HTH

robertz
fuente
2
hm .. ¿Cuál es la mejor manera? Debo pasar datos a ViewState desde la sesión en el controlador, ¿no?
RredCat
2
y podrías explicar las restricciones de este método?
RredCat
1
Creo que quiso decir que es mejor tener métodos de lectura / escritura. Dependiendo del uso de concurrencia / hilo, también puede necesitar bloqueos en esos métodos de lectura / escritura para evitar una condición de carrera.
DeepSpace101
13

Bueno, en mi humilde opinión ..

  1. nunca haga referencia a una sesión dentro de su vista / página maestra
  2. minimizar su uso de la sesión. MVC proporciona obj TempData para esto, que es básicamente una sesión que vive para un solo viaje al servidor.

Con respecto al n. ° 1, tengo una vista maestra fuertemente tipada que tiene una propiedad para acceder a lo que sea que represente el objeto Session ... en mi caso, la vista maestra tipada es genérica, lo que me da cierta flexibilidad con respecto a las páginas de vista fuertemente tipadas

ViewMasterPage<AdminViewModel>

AdminViewModel
{
    SomeImportantObjectThatWasInSession ImportantObject
}

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

y entonces...

ViewPage<AdminViewModel<U>>
E Rolnicki
fuente
7

Aunque no sé acerca de asp.net mvc, esto es lo que deberíamos hacer en un sitio web .net normal. También debería funcionar para asp.net mvc.

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

Pondría esto dentro de un método para facilitar el acceso. HTH

Punto net
fuente
7

Mi forma de acceder a las sesiones es escribir una clase auxiliar que encapsule los diversos nombres de campo y sus tipos. Espero que este ejemplo ayude:

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; }
        }

    }
}
Daniel
fuente
Me gusta su respuesta ... ¿podría dar más detalles sobre lo que está sucediendo ... y por qué este es un mejor enfoque opuesto a las otras respuestas en este hilo?
Chef_Code
6

Grandes respuestas de los muchachos, pero les advierto que no confíen siempre en la sesión. Es rápido y fácil hacerlo, y por supuesto funcionaría, pero no sería excelente en todas las circunstancias.

Por ejemplo, si se encuentra con un escenario en el que su alojamiento no permite el uso de la sesión, o si se encuentra en una granja de servidores web, o en el ejemplo de una aplicación de SharePoint compartida.

Si desea una solución diferente, puede utilizar un contenedor IOC como Castle Windsor , crear una clase de proveedor como envoltorio y luego mantener una instancia de su clase utilizando el estilo de vida por solicitud o sesión, según sus requisitos.

El COI se aseguraría de que se devuelva la misma instancia cada vez.

Más complicado sí, si necesita una solución simple, simplemente use la sesión.

Estos son algunos ejemplos de implementación a continuación por interés.

Con este método, podría crear una clase de proveedor en la línea de:

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

    public string CustomClass { get; private set; }
}

Y regístralo algo como:

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

Puede usar ViewModelBase como clase base para todos los modelos, esta clase se encargará de extraer datos de la sesión

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

Puede escribir un método de extensión en HttpContextBase para tratar con datos de sesión

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;
}

Use esto como a continuación en el controlador

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

El segundo argumento es opcional, se usará para llenar datos de sesión para esa clave cuando el valor no esté presente en la sesión.

Ajay Kelkar
fuente