Zmienne sesji w ASP.NET MVC

169

Piszę aplikację internetową, która pozwoli użytkownikowi przeglądać wiele stron internetowych w ramach serwisu, wykonując określone żądania. Wszystkie informacje wprowadzone przez użytkownika będą przechowywane w utworzonym przeze mnie obiekcie. Problem polega na tym, że potrzebuję dostępu do tego obiektu z dowolnej części strony internetowej i tak naprawdę nie znam najlepszego sposobu, aby to osiągnąć. Wiem, że jednym rozwiązaniem jest użycie zmiennych sesji, ale nie wiem, jak ich używać w asp .net MVC. A gdzie zadeklarowałbym zmienną sesyjną? Czy jest inny sposób?

Draco
źródło
3
Mieszasz koncepcje witryny sieci Web i aplikacji sieci Web ... to nie to samo.
adripanico
1
Brzmi jak potrzeba bazy danych
Coops,
1
Możliwy duplikat Jak używać sesji w aplikacji ASP.NET MVC 4?
Michael Freidgeim

Odpowiedzi:

123

Myślę, że będziesz chciał pomyśleć o tym, czy rzeczy naprawdę pasują do stanu sesji. To jest coś, co robię od czasu do czasu i jest to ładne, mocno wpisane podejście do całej sprawy, ale powinieneś być ostrożny, umieszczając rzeczy w kontekście sesji. Nie wszystko powinno tam być tylko dlatego, że należy do jakiegoś użytkownika.

w global.asax przechwyć zdarzenie OnSessionStart

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

Z dowolnego miejsca w kodzie, w którym właściwość HttpContext.Current! = Null, można pobrać ten obiekt. Robię to metodą rozszerzającą.

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

W ten sposób możesz w kodzie

void OnLoad(...)
{
    var sessionObj = HttpContext.Current.GetMySessionObject();
    // do something with 'sessionObj'
}
John Leidegren
źródło
32
Jeśli używany jest ASP MVC, lepiej jest nie używać rzeczywistego obiektu Session z HttpContext.Current.Session, ale użyć nowego HttpSessionStateWrapper i HttpSessionStateBase z System.Web.Abstractions.dll, a następnie użyć Factory lub DI w celu uzyskania Session.
Paul
6
Jak przypisujesz coś do zmiennej sesji? (w przeciwieństwie do samego dostępu)
raklos
31
Osoby próbujące dowiedzieć się, czym jest zdarzenie „OnSessionStart” i jak je „podłączyć”, zobacz stackoverflow.com/questions/1531125/…
Cephron
5
@Paul Czy możesz podać przykład? Nie mogę znaleźć żadnych przykładów użycia HttpSessionStateWrapper.
Joseph Woodward,
4
@AjayKelkar W tym wątku komentarza zasugerowano: "Jeśli używany jest ASP MVC, lepiej nie używać rzeczywistego obiektu Session z HttpContext.Current.Session, ale używać nowego HttpSessionStateWrapper i HttpSessionStateBase", co sugeruje, że twoje odpowiedzi nie są lepsze
Coops
48

Odpowiedź tutaj jest prawidłowa, jednak starałem się zaimplementować ją w aplikacji ASP.NET MVC 3. Chciałem uzyskać dostęp do obiektu sesji w kontrolerze i nie mogłem zrozumieć, dlaczego ciągle otrzymuję komunikat „Instancja nie jest ustawiona na wystąpienie błędu obiektu”. Zauważyłem, że w kontrolerze, gdy próbowałem uzyskać dostęp do sesji, wykonując następujące czynności, nadal otrzymywałem ten błąd. Wynika to z faktu, że this.HttpContext jest częścią obiektu Controller.

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

Jednak chciałem mieć HttpContext, który jest częścią przestrzeni nazw System.Web, ponieważ jest to ten, który odpowiedź sugeruje powyżej, aby użyć w Global.asax.cs. Musiałem więc wyraźnie wykonać następujące czynności:

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

to mi pomogło, nie jestem pewien, czy zrobiłem coś, co nie jest tutaj MO, ale mam nadzieję, że to komuś pomoże!

Tomasz Iniewicz
źródło
6
System.Web.HttpContext.Current.Session ["blah"] = wartość
Tomasz Iniewicz
21

Ponieważ nie lubię widzieć „HTTPContext.Current.Session” o tym miejscu, używam wzorca singleton, aby uzyskać dostęp do zmiennych sesji, co daje łatwy dostęp do zbioru danych o silnym typie.

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

    // ...

wtedy możesz uzyskać dostęp do swoich danych z dowolnego miejsca:

SessionSingleton.Current.SessionVariable = "Hello, World!";
Martwy Rabit
źródło
2
Więc ta klasa ma dwa zadania: utrzymywać pojedynczą instancję i przechowywać zmienne ... Użyłbym kontenera IOC, aby mieć singleton.
Jowen
1
Jeśli masz już jedną konfigurację, prawdopodobnie stworzyłbym również pełnoprawną usługę sesji z możliwością wstrzykiwania, chociaż spójność jest prawdopodobnie największą zaletą, byłbym bardziej skłonny do używania tego kodu dla małych aplikacji sieciowych z zestawem funkcji .. webwizardów, jeśli wolisz.
Dead.Rabit
14

Jeśli używasz asp.net mvc, oto prosty sposób na uzyskanie dostępu do sesji.

Z kontrolera:

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

Z widoku:

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

Z pewnością nie jest to najlepszy sposób uzyskiwania dostępu do zmiennych sesji, ale jest to droga bezpośrednia. Dlatego używaj go ostrożnie (najlepiej podczas szybkiego prototypowania) i używaj Wrapper / Container i OnSessionStart, gdy okaże się to odpowiednie.

HTH

robertz
źródło
2
hm .. Jaki jest najlepszy sposób? Powinienem przekazać dane do ViewState z sesji na kontrolerze, czy nie?
RredCat
2
i czy mógłbyś wyjaśnić ograniczenia tej metody?
RredCat
1
Myślę, że miał na myśli, że najlepiej jest mieć metody odczytu / zapisu. W zależności od użycia współbieżności / wątku możesz również potrzebować blokad w tych metodach odczytu / zapisu, aby uniknąć sytuacji wyścigu.
DeepSpace101,
13

Cóż, IMHO ..

  1. nigdy nie odwołuj się do sesji na stronie widoku / wzorcowej
  2. zminimalizować wykorzystanie sesji. MVC udostępnia TempData obj do tego, który jest w zasadzie sesją, która trwa pojedynczą podróż do serwera.

Jeśli chodzi o # 1, mam silnie wpisany widok główny, który ma właściwość dostępu do wszystkiego, co reprezentuje obiekt Session ... w moim przypadku mocno wpisany widok główny jest ogólny, co daje mi pewną elastyczność w odniesieniu do stron widoku z silnym typem

ViewMasterPage<AdminViewModel>

AdminViewModel
{
    SomeImportantObjectThatWasInSession ImportantObject
}

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

i wtedy...

ViewPage<AdminViewModel<U>>
E Rolnicki
źródło
7

Chociaż nie wiem o mvc asp.net, ale to właśnie powinniśmy zrobić w normalnej witrynie sieci Web .NET. Powinien działać również dla asp.net mvc.

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

Umieściłbyś to w metodzie łatwego dostępu. HTH

DotNET
źródło
7

Moim sposobem na dostęp do sesji jest napisanie klasy pomocniczej, która zawiera różne nazwy pól i ich typy. Mam nadzieję, że ten przykład pomoże:

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
źródło
Podoba mi się twoja odpowiedź ... czy mógłbyś dokładniej wyjaśnić, co się dzieje ... i dlaczego jest to lepsze podejście niż inne odpowiedzi w tym wątku.
Chef_Code,
6

Świetne odpowiedzi od chłopaków, ale ostrzegam Cię przed zawsze poleganiem na sesji. Można to zrobić szybko i łatwo i oczywiście zadziała, ale nie będzie świetny w każdych okolicznościach.

Na przykład, jeśli napotkasz scenariusz, w którym Twój hosting nie zezwala na użycie sesji lub jeśli jesteś w farmie sieci Web lub w przykładzie udostępnionej aplikacji SharePoint.

Jeśli potrzebujesz innego rozwiązania, możesz spojrzeć na użycie kontenera IOC, takiego jak Castle Windsor , tworząc klasę dostawcy jako opakowanie, a następnie utrzymując jedną instancję swojej klasy przy użyciu stylu życia na żądanie lub sesji w zależności od wymagań.

MKOl zapewni, że za każdym razem zwracana będzie ta sama instancja.

Bardziej skomplikowane tak, jeśli potrzebujesz prostego rozwiązania, po prostu skorzystaj z sesji.

Oto kilka przykładów realizacji poniżej, które nie są interesujące.

Korzystając z tej metody, można utworzyć klasę dostawcy w następujący sposób:

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

    public string CustomClass { get; private set; }
}

I zarejestruj to na przykład:

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

Możesz użyć ViewModelBase jako klasy bazowej dla wszystkich modeli, ta klasa zajmie się pobieraniem danych z sesji

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

Możesz napisać metodę rozszerzającą w HttpContextBase, aby radzić sobie z danymi sesji

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

Użyj tego jak poniżej w kontrolerze

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

Drugi argument jest opcjonalny, zostanie użyty do wypełnienia danych sesji dla tego klucza, gdy wartość nie jest obecna w sesji.

Ajay Kelkar
źródło