Variáveis ​​de sessão no ASP.NET MVC

169

Estou escrevendo um aplicativo da Web que permitirá ao usuário navegar em várias páginas da Web no site, fazendo determinadas solicitações. Todas as informações que o usuário inserir serão armazenadas em um objeto que eu criei. O problema é que eu preciso que esse objeto seja acessado de qualquer parte do site e realmente não sei a melhor maneira de fazer isso. Eu sei que uma solução é usar variáveis ​​de sessão, mas não sei como usá-las no asp .net MVC. E onde eu declararia uma variável de sessão? Existe alguma outra maneira?

Draco
fonte
3
Você está misturando os conceitos de site e aplicativo da Web ... eles não são a mesma coisa.
Adripanico
1
Soa como a necessidade de um banco de dados
Coops
1
Possível duplicata de Como usar sessões em um aplicativo ASP.NET MVC 4?
Michael Freidgeim

Respostas:

123

Eu acho que você vai querer pensar se as coisas realmente pertencem a um estado de sessão. Isso é algo que eu me vejo fazendo de vez em quando e é uma boa abordagem fortemente tipada para a coisa toda, mas você deve ter cuidado ao colocar as coisas no contexto da sessão. Nem tudo deve estar lá apenas porque pertence a algum usuário.

no global.asax hook o evento OnSessionStart

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

De qualquer lugar no código em que a propriedade HttpContext.Current! = Null, você pode recuperar esse objeto. Eu faço isso com um método de extensão.

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

Desta forma, você pode no código

void OnLoad(...)
{
    var sessionObj = HttpContext.Current.GetMySessionObject();
    // do something with 'sessionObj'
}
John Leidegren
fonte
32
Se o ASP MVC estiver sendo usado, é preferível não usar o objeto Session real de HttpContext.Current.Session, mas usar o novo HttpSessionStateWrapper & HttpSessionStateBase de System.Web.Abstractions.dll e, em seguida, usar um Factory ou DI para obter a sessão.
Paul Paul
6
Como você atribui algo à variável de sessão? (em oposição a apenas acesso)
raklos
31
Para pessoas que tentam descobrir o que é o evento "OnSessionStart" e como você o "liga", consulte stackoverflow.com/questions/1531125/…
Cephron
5
@Paul Você é capaz de fornecer um exemplo? Não consigo encontrar nenhum exemplo de uso do HttpSessionStateWrapper.
Joseph Woodward
4
@AjayKelkar Este tópico de comentário sugeriu "Se o ASP MVC estiver sendo usado, é preferível não usar o objeto Session real de HttpContext.Current.Session, mas usar o novo HttpSessionStateWrapper & HttpSessionStateBase", que sugere que suas respostas não são melhores
Coops
48

A resposta aqui está correta, no entanto, lutei para implementá-lo em um aplicativo ASP.NET MVC 3. Eu queria acessar um objeto Session em um controlador e não conseguia descobrir por que continuava recebendo uma "Instância não definida para uma instância de um erro de Objeto". O que notei é que, em um controlador, quando tentei acessar a sessão, continuei recebendo esse erro. Isso se deve ao fato de que this.HttpContext faz parte do objeto Controller.

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

No entanto, o que eu queria era o HttpContext que faz parte do espaço para nome System.Web porque esse é o sugerido pela resposta acima no Global.asax.cs. Então eu tive que fazer explicitamente o seguinte:

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

isso me ajudou, não tenho certeza se fiz algo que não seja MO por aqui, mas espero que ajude alguém!

Tomasz Iniewicz
fonte
6
System.Web.HttpContext.Current.Session ["blah"] = value
Tomasz Iniewicz
21

Como não gosto de ver "HTTPContext.Current.Session" sobre o local, uso um padrão singleton para acessar variáveis ​​de sessão, oferecendo um fácil acesso a um pacote de dados fortemente tipado.

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

    // ...

então você pode acessar seus dados de qualquer lugar:

SessionSingleton.Current.SessionVariable = "Hello, World!";
Dead.Rabit
fonte
2
Portanto, essa classe tem duas responsabilidades: manter uma única instância e armazenar variáveis ​​... Eu usaria um contêiner IOC para ter um singleton.
Jowen
1
Se você já tem uma configuração, eu provavelmente também faria um serviço de sessão injetável completo, consistências provavelmente a maior vantagem, eu estaria mais inclinado a usar esse código para pequenos aplicativos de recursos da Web. Assistentes da Web, se desejar.
Dead.Rabit
14

Se você estiver usando o asp.net mvc, aqui está uma maneira simples de acessar a sessão.

De um controlador:

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

De uma vista:

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

Definitivamente, essa não é a melhor maneira de acessar suas variáveis ​​de sessão, mas é uma rota direta. Portanto, use-o com cuidado (de preferência durante a prototipagem rápida) e use um Wrapper / Container e OnSessionStart quando for apropriado.

HTH

robertz
fonte
2
hm .. Qual é o melhor caminho? Eu deveria passar dados para o ViewState da Session no controlador, não deveria?
RredCat
2
e você poderia explicar constrições desse método?
RredCat
1
Eu acho que ele quis dizer que é melhor ter métodos de leitura / gravação. Dependendo do uso da simultaneidade / encadeamento, você também pode precisar de bloqueios nos métodos de leitura / gravação para evitar uma condição de corrida.
precisa saber é o seguinte
13

Bem, IMHO ..

  1. nunca faça referência a uma sessão na sua visualização / página principal
  2. minimizar sua utilização da sessão. O MVC fornece o objeto TempData para isso, que é basicamente uma sessão que dura uma única viagem ao servidor.

No que diz respeito ao item 1, eu tenho uma Visualização mestra fortemente tipada, que possui uma propriedade para acessar o que o objeto Session representa .... no meu caso, a Visualização mestra tipicamente digitada é genérica, o que me dá alguma flexibilidade em relação às Páginas vitais fortemente tipadas

ViewMasterPage<AdminViewModel>

AdminViewModel
{
    SomeImportantObjectThatWasInSession ImportantObject
}

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

e depois...

ViewPage<AdminViewModel<U>>
E Rolnicki
fonte
7

Embora eu não conheça o asp.net mvc, é o que devemos fazer em um site .net normal. Também deve funcionar para o asp.net mvc.

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

Você colocaria isso dentro de um método para facilitar o acesso. HTH

Ponto Net
fonte
7

Minha maneira de acessar as sessões é escrever uma classe auxiliar que encapsule os vários nomes de campos e seus tipos. Espero que este exemplo ajude:

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
fonte
Gosto da sua resposta ... você poderia elaborar mais detalhadamente o que está acontecendo ... e por que essa é uma abordagem melhor em oposição às outras respostas neste tópico.
Chef_Code 16/09
6

Ótimas respostas dos rapazes, mas eu aconselho você a não confiar sempre na Sessão. É rápido e fácil, e é claro que funcionaria, mas não seria ótimo em todas as circunstâncias.

Por exemplo, se você se deparar com um cenário em que sua hospedagem não permita o uso da sessão, ou se você estiver em um farm da web ou no exemplo de um aplicativo compartilhado do SharePoint.

Se você queria uma solução diferente, poderia usar um contêiner do COI como o Castle Windsor , criar uma classe de provedor como um wrapper e, em seguida, manter uma instância da sua classe usando o estilo de vida por solicitação ou sessão, dependendo de seus requisitos.

O COI garantiria que a mesma instância fosse retornada toda vez.

Mais complicado, sim, se você precisar de uma solução simples, basta usar a sessão.

Aqui estão alguns exemplos de implementação abaixo sem interesse.

Usando este método, você pode criar uma classe de provedor ao longo das linhas de:

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

    public string CustomClass { get; private set; }
}

E registre algo como:

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

Você pode usar o ViewModelBase como classe base para todos os modelos; essa classe cuidará de extrair dados da sessão

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

Você pode escrever um método de extensão no HttpContextBase para lidar com os dados da sessão

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 isso como abaixo no controlador

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

O segundo argumento é opcional, ele será usado para preencher os dados da sessão dessa chave quando o valor não estiver presente na sessão.

Ajay Kelkar
fonte