Variabili di sessione in ASP.NET MVC


169

Sto scrivendo un'applicazione Web che consentirà a un utente di navigare su più pagine Web all'interno del sito Web effettuando determinate richieste. Tutte le informazioni immesse dall'utente verranno archiviate in un oggetto che ho creato. Il problema è che ho bisogno di accedere a questo oggetto da qualsiasi parte del sito Web e non conosco davvero il modo migliore per farlo. So che una soluzione è usare le variabili di sessione ma non so come usarle in asp .net MVC. E dove dovrei dichiarare una variabile di sessione? C'è un altro modo?


3
Stai mescolando i concetti del sito Web e dell'applicazione Web ... non sono la stessa cosa.
adripanico,

1
Sembra una necessità di un database
Coops

Risposte:


123

Penserei che vorrai pensare se le cose appartengono davvero a uno stato di sessione. Questo è qualcosa che mi ritrovo a fare di tanto in tanto ed è un bell'approccio fortemente tipizzato all'intera cosa, ma dovresti stare attento quando metti le cose nel contesto della sessione. Non tutto dovrebbe essere lì solo perché appartiene a qualche utente.

in global.asax agganciare l'evento OnSessionStart

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

Da qualsiasi parte del codice in cui la proprietà HttpContext.Current! = Null è possibile recuperare quell'oggetto. Lo faccio con un metodo di estensione.

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

In questo modo è possibile nel codice

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

32
Se si utilizza ASP MVC, è preferibile non utilizzare l'oggetto Session effettivo da HttpContext.Current.Session ma utilizzare il nuovo HttpSessionStateWrapper e HttpSessionStateBase da System.Web.Abstractions.dll, quindi utilizzare Factory o DI per ottenere la sessione.
Paul,

6
Come si assegna qualcosa alla variabile di sessione? (al contrario del solo accesso)
raklos

31
Per le persone a cercare di capire ciò che l'evento "OnSessionStart" è e come si "gancio", vedi stackoverflow.com/questions/1531125/...
Cephron

5
@Paul Sei in grado di fornire un esempio? Non riesco a trovare alcun esempio di utilizzo di HttpSessionStateWrapper.
Joseph Woodward,

4
@AjayKelkar Questo thread di commenti ha suggerito "Se si utilizza ASP MVC, è preferibile non utilizzare l'oggetto Session effettivo da HttpContext.Current.Session ma utilizzare il nuovo HttpSessionStateWrapper & HttpSessionStateBase" che suggerisce che le risposte non sono migliori
Coops

48

La risposta qui è corretta, tuttavia ho faticato a implementarla in un'app ASP.NET MVC 3. Volevo accedere a un oggetto Session in un controller e non riuscivo a capire perché continuassi a ottenere un "Instance non impostato su un'istanza di un errore Object". Quello che ho notato è che in un controller, quando ho provato ad accedere alla sessione procedendo come segue, ho continuato a ricevere quell'errore. Ciò è dovuto al fatto che this.HttpContext fa parte dell'oggetto Controller.

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

Tuttavia, quello che volevo era l'HttpContext che fa parte dello spazio dei nomi System.Web perché questo è quello che la risposta sopra suggerisce di usare in Global.asax.cs. Quindi ho dovuto fare esplicitamente quanto segue:

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

questo mi ha aiutato, non sono sicuro di aver fatto qualcosa che non sia MO qui, ma spero che aiuti qualcuno!


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

21

Poiché non mi piace vedere "HTTPContext.Current.Session" sul luogo, uso un modello singleton per accedere alle variabili di sessione, ti dà un facile accesso a una borsa di dati fortemente tipizzata.

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

    // ...

quindi puoi accedere ai tuoi dati da qualsiasi luogo:

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

2
Quindi questa classe ha due responsabilità: mantenere una singola istanza e memorizzare le variabili ... Userei un contenitore IOC per avere un singleton.
Jowen,

1
Se hai già una configurazione, probabilmente farei anche un servizio di sessione iniettabile a tutti gli effetti, tuttavia, le consistenze probabilmente sono il vantaggio più grande, sarei più propenso a usare questo codice per piccole webapps di insiemi di funzionalità .. Webwizards se vuoi.
Dead.Rabit

14

Se stai usando asp.net mvc, ecco un modo semplice per accedere alla sessione.

Da un controller:

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

Da una vista:

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

Questo non è sicuramente il modo migliore per accedere alle variabili della sessione, ma è un percorso diretto. Quindi usalo con cautela (preferibilmente durante la prototipazione rapida) e usa un Wrapper / Container e OnSessionStart quando diventa appropriato.

HTH


2
hm .. Qual è il modo migliore? Dovrei passare i dati a ViewState dalla sessione sul controller, no?
RredCat,

2
e potresti spiegare le costrizioni di questo metodo?
RredCat,

1
Penso che volesse dire che è meglio avere metodi di lettura / scrittura. A seconda dell'uso di concorrenza / thread, potresti anche aver bisogno di blocchi in quei metodi di lettura / scrittura per evitare una condizione di competizione.
DeepSpace101

13

Bene, IMHO ..

  1. non fare mai riferimento a una sessione all'interno della pagina di visualizzazione / principale
  2. minimizza il tuo utilizzo della sessione. MVC fornisce TempData obj per questo, che è fondamentalmente una sessione che vive per un singolo viaggio sul server.

Per quanto riguarda il n. 1, ho una vista principale fortemente tipizzata che ha una proprietà per accedere a qualunque oggetto Session rappresenti .... nel mio caso la vista principale tipizzata in modo massiccio è generica, il che mi dà una certa flessibilità riguardo alle pagine di vista fortemente tipizzate

ViewMasterPage<AdminViewModel>

AdminViewModel
{
    SomeImportantObjectThatWasInSession ImportantObject
}

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

e poi...

ViewPage<AdminViewModel<U>>

7

Anche se non conosco asp.net mvc, ma questo è ciò che dovremmo fare in un normale sito Web .net. Dovrebbe funzionare anche per asp.net mvc.

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

Lo inseriresti in un metodo per un facile accesso. HTH



7

Il mio modo di accedere alle sessioni è scrivere una classe di supporto che incapsuli i vari nomi dei campi e i loro tipi. Spero che questo esempio aiuti:

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

    }
}

Mi piace la tua risposta ... potresti approfondire cosa sta succedendo ... e perché questo è un approccio migliore rispetto alle altre risposte su questo thread.
Chef_Code

6

Grandi risposte da parte dei ragazzi, ma ti metterei in guardia dal fare sempre affidamento sulla Sessione. È rapido e facile farlo, e ovviamente funzionerebbe ma non sarebbe eccezionale in tutte le circostanze.

Ad esempio, se ti imbatti in uno scenario in cui il tuo hosting non consente l'utilizzo della sessione o se ti trovi in ​​una Web farm o nell'esempio di un'applicazione SharePoint condivisa.

Se si desidera una soluzione diversa, è possibile utilizzare un contenitore IOC come Castle Windsor , creando una classe provider come wrapper e mantenendo un'istanza della classe utilizzando lo stile di vita per richiesta o sessione in base alle proprie esigenze.

Il CIO assicurerebbe che la stessa istanza venga restituita ogni volta.

Più complicato sì, se hai bisogno di una soluzione semplice basta usare la sessione.

Ecco alcuni esempi di implementazione di seguito per interesse.

Utilizzando questo metodo è possibile creare una classe di provider sulla falsariga di:

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

    public string CustomClass { get; private set; }
}

E registralo come:

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

4

Puoi utilizzare ViewModelBase come classe di base per tutti i modelli, questa classe si occuperà di estrarre i dati dalla sessione

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

È possibile scrivere un metodo di estensione su HttpContextBase per gestire i dati della sessione

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

Usa questo come sotto nel controller

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

Il secondo argomento è facoltativo e verrà utilizzato riempire i dati della sessione per quella chiave quando il valore non è presente nella sessione.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.