Variables de session dans ASP.NET MVC

169

J'écris une application Web qui permettra à un utilisateur de naviguer sur plusieurs pages Web du site Web en effectuant certaines demandes. Toutes les informations saisies par l'utilisateur seront stockées dans un objet que j'ai créé. Le problème est que j'ai besoin que cet objet soit accessible depuis n'importe quelle partie du site Web et je ne connais pas vraiment la meilleure façon d'y parvenir. Je sais qu'une solution consiste à utiliser des variables de session mais je ne sais pas comment les utiliser dans asp .net MVC. Et où déclarerais-je une variable de session? Est-ce qu'il y a un autre moyen?

Draco
la source
3
Vous mélangez les concepts de site Web et d'application Web ... ce n'est pas la même chose.
adripanico
1
Sonne comme un besoin d'une base de données
Coops

Réponses:

123

Je pense que vous voudrez vous demander si les choses appartiennent vraiment à un état de session. C'est quelque chose que je me surprends à faire de temps en temps et c'est une belle approche fortement typée de l'ensemble, mais vous devez être prudent lorsque vous mettez les choses dans le contexte de la session. Tout ne devrait pas être là simplement parce qu'il appartient à un utilisateur.

dans global.asax hook l'événement OnSessionStart

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

De n'importe où dans le code où la propriété HttpContext.Current! = Null, vous pouvez récupérer cet objet. Je fais cela avec une méthode d'extension.

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

De cette façon, vous pouvez dans le code

void OnLoad(...)
{
    var sessionObj = HttpContext.Current.GetMySessionObject();
    // do something with 'sessionObj'
}
John Leidegren
la source
32
Si ASP MVC est utilisé, il est préférable de ne pas utiliser l'objet Session réel de HttpContext.Current.Session mais d'utiliser le nouveau HttpSessionStateWrapper & HttpSessionStateBase de System.Web.Abstractions.dll puis utiliser une usine ou DI pour obtenir la session.
Paul
6
Comment attribuez-vous quelque chose à la variable de session? (par opposition à simplement accéder)
raklos
31
Pour les personnes qui essaient de comprendre ce qu'est l'événement "OnSessionStart" et comment vous le "raccordez", consultez stackoverflow.com/questions/1531125
...
5
@Paul Pouvez-vous donner un exemple? Je n'arrive pas à trouver d'exemples d'utilisation de HttpSessionStateWrapper.
Joseph Woodward
4
@AjayKelkar Ce fil de discussion suggérait "Si ASP MVC est utilisé, il est préférable de ne pas utiliser l'objet Session réel de HttpContext.Current.Session mais d'utiliser le nouveau HttpSessionStateWrapper & HttpSessionStateBase" qui suggère que vos réponses ne sont pas meilleures
Coops
48

La réponse ici est correcte, j'ai cependant eu du mal à l'implémenter dans une application ASP.NET MVC 3. Je voulais accéder à un objet Session dans un contrôleur et je ne pouvais pas comprendre pourquoi je continuais à obtenir une "Instance non définie sur une instance d'une erreur d'objet". Ce que j'ai remarqué, c'est que dans un contrôleur, lorsque j'ai essayé d'accéder à la session en procédant comme suit, j'ai continué à obtenir cette erreur. Cela est dû au fait que this.HttpContext fait partie de l'objet Controller.

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

Cependant, ce que je voulais, c'était le HttpContext qui fait partie de l'espace de noms System.Web car c'est celui que la réponse ci-dessus suggère d'utiliser dans Global.asax.cs. J'ai donc dû faire explicitement ce qui suit:

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

cela m'a aidé, je ne sais pas si j'ai fait quelque chose qui ne soit pas MO ici, mais j'espère que cela aide quelqu'un!

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

Parce que je n'aime pas voir "HTTPContext.Current.Session" à propos de l'endroit, j'utilise un modèle singleton pour accéder aux variables de session, cela vous donne un sac de données fortement typé facile d'accès.

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

    // ...

alors vous pouvez accéder à vos données de n'importe où:

SessionSingleton.Current.SessionVariable = "Hello, World!";
Mort.Rabit
la source
2
Cette classe a donc deux responsabilités: maintenir une seule instance et stocker des variables ... J'utiliserais un conteneur IOC pour avoir un singleton.
Jowen
1
Si vous avez déjà une configuration, je ferais probablement aussi un service de session injectable à part entière, cependant, les consistances sont probablement le plus grand avantage, je serais plus enclin à utiliser ce code pour de petites applications Web de fonctionnalités ... des webwizards si vous voulez.
Dead.Rabit
14

Si vous utilisez asp.net mvc, voici un moyen simple d'accéder à la session.

D'un contrôleur:

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

Depuis une vue:

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

Ce n'est certainement pas la meilleure façon d'accéder à vos variables de session, mais c'est une route directe. Utilisez-le donc avec précaution (de préférence lors du prototypage rapide), et utilisez un Wrapper / Container et OnSessionStart quand cela devient approprié.

HTH

robertz
la source
2
hm .. Quelle est la meilleure façon? Je devrais transmettre des données à ViewState depuis Session sur le contrôleur, n'est-ce pas?
RredCat
2
et pourriez-vous expliquer les contraintes de cette méthode?
RredCat
1
Je pense qu'il voulait dire qu'il était préférable d'avoir des méthodes de lecture / écriture. En fonction de l'utilisation de l'accès concurrentiel / du thread, vous pouvez également avoir besoin de verrous dans ces méthodes de lecture / écriture pour éviter une condition de concurrence.
DeepSpace101
13

Eh bien, à mon humble avis ...

  1. ne jamais référencer une session dans votre vue / page maître
  2. minimisez votre utilisation de Session. MVC fournit l'obj TempData pour cela, qui est essentiellement une session qui vit pour un seul voyage vers le serveur.

En ce qui concerne le n ° 1, j'ai une vue principale fortement typée qui a la propriété d'accéder à tout ce que représente l'objet Session .... dans mon cas, la vue principale fortement typée est générique, ce qui me donne une certaine flexibilité en ce qui concerne les pages d'affichage fortement typées

ViewMasterPage<AdminViewModel>

AdminViewModel
{
    SomeImportantObjectThatWasInSession ImportantObject
}

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

puis...

ViewPage<AdminViewModel<U>>
E Rolnicki
la source
7

Bien que je ne connaisse pas asp.net mvc, c'est ce que nous devrions faire sur un site Web .net normal. Cela devrait également fonctionner pour asp.net mvc.

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

Vous mettriez ceci dans une méthode pour un accès facile. HTH

Point net
la source
7

Ma façon d'accéder aux sessions est d'écrire une classe d'assistance qui encapsule les différents noms de champs et leurs types. J'espère que cet exemple aidera:

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
la source
J'aime votre réponse ... pourriez-vous préciser davantage ce qui se passe ... et pourquoi c'est une meilleure approche par rapport aux autres réponses sur ce fil.
Chef_Code
6

Excellentes réponses des gars, mais je vous déconseille de toujours vous fier à la session. C'est rapide et facile à faire, et cela fonctionnerait bien sûr, mais ce ne serait pas génial dans toutes les circonstances.

Par exemple, si vous rencontrez un scénario dans lequel votre hébergement n'autorise pas l'utilisation de session, ou si vous êtes sur une batterie de serveurs Web, ou dans l'exemple d'une application SharePoint partagée.

Si vous souhaitez une solution différente, vous pouvez envisager d'utiliser un conteneur IOC tel que Castle Windsor , en créant une classe de fournisseur en tant que wrapper, puis en conservant une instance de votre classe en utilisant le mode de vie par requête ou par session en fonction de vos besoins.

Le CIO veillerait à ce que la même instance soit renvoyée à chaque fois.

Plus compliqué oui, si vous avez besoin d'une solution simple, utilisez simplement la session.

Voici quelques exemples de mise en œuvre ci-dessous par intérêt.

En utilisant cette méthode, vous pouvez créer une classe de fournisseur du type:

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

    public string CustomClass { get; private set; }
}

Et enregistrez-le quelque chose comme:

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

Vous pouvez utiliser ViewModelBase comme classe de base pour tous les modèles, cette classe se chargera d'extraire les données de la session

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

Vous pouvez écrire une méthode d'extension sur HttpContextBase pour gérer les données de session

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

Utilisez ceci comme ci-dessous dans le contrôleur

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

Le deuxième argument est facultatif, il sera utilisé pour remplir les données de session pour cette clé lorsque la valeur n'est pas présente dans la session.

Ajay Kelkar
la source