Переменные сеанса в ASP.NET MVC

169

Я пишу веб-приложение, которое позволит пользователю просматривать несколько веб-страниц на сайте, делая определенные запросы. Вся информация, которую вводит пользователь, будет храниться в объекте, который я создал. Проблема в том, что мне нужен доступ к этому объекту из любой части сайта, и я не знаю, как лучше всего это сделать. Я знаю, что одним из решений является использование переменных сеанса, но я не знаю, как их использовать в asp .net MVC. И где бы я объявил переменную сеанса? Есть ли другой путь?

Дракон
источник
3
Вы смешиваете концепции веб-сайта и веб-приложения ... это не одно и то же.
Adripanico
1
Похоже на необходимость базы данных
Coops
1
Возможный дубликат Как использовать сеансы в приложении ASP.NET MVC 4?
Майкл Фрейдгейм

Ответы:

123

Я думаю, вы захотите подумать, действительно ли вещи принадлежат состоянию сеанса. Это то, что я делаю время от времени, и это хороший строго типизированный подход ко всему, но вы должны быть осторожны, когда помещаете вещи в контекст сеанса. Не все должно быть там только потому, что оно принадлежит какому-то пользователю.

в global.asax перехватить событие OnSessionStart

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

Из любого места в коде, где свойство HttpContext.Current! = Null, вы можете получить этот объект. Я делаю это с помощью метода расширения.

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

Таким образом, вы можете в коде

void OnLoad(...)
{
    var sessionObj = HttpContext.Current.GetMySessionObject();
    // do something with 'sessionObj'
}
Джон Лейдгрен
источник
32
Если используется ASP MVC, то предпочтительно не использовать фактический объект Session из HttpContext.Current.Session, а использовать новый HttpSessionStateWrapper & HttpSessionStateBase из System.Web.Abstractions.dll, а затем использовать Factory или DI для получения Session.
Пол
6
Как вы назначаете что-то переменной сеанса? (в отличие от простого доступа)
raklos
31
Для людей , которые пытаются выяснить , что это событие «OnSessionStart» и как вы «крюк» это, см stackoverflow.com/questions/1531125/...
Cephron
5
@Paul Можете ли вы привести пример? Я не могу найти какие-либо примеры использования HttpSessionStateWrapper.
Джозеф Вудворд
4
@AjayKelkar В этой ветке комментариев предлагалось: «Если ASP MVC используется, то желательно не использовать реальный объект Session из HttpContext.Current.Session, а использовать новый HttpSessionStateWrapper & HttpSessionStateBase», который предлагает ваши ответы не лучше
Coops
48

Ответ здесь правильный, однако я изо всех сил пытался реализовать его в приложении ASP.NET MVC 3. Я хотел получить доступ к объекту Session в контроллере и не мог понять, почему я продолжал получать «Экземпляр не установлен в экземпляр ошибки объекта». Я заметил, что в контроллере, когда я пытался получить доступ к сеансу, выполняя следующее, я продолжал получать эту ошибку. Это связано с тем, что this.HttpContext является частью объекта Controller.

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

Однако я хотел, чтобы был HttpContext, который является частью пространства имен System.Web, потому что именно этот ответ предлагает использовать в Global.asax.cs ответ выше. Поэтому я должен был сделать следующее:

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

это помогло мне, не уверен, что я сделал что-то, кроме МО здесь, но я надеюсь, что это кому-то поможет!

Томаш Иневич
источник
6
System.Web.HttpContext.Current.Session ["blah"] = значение
Томаш Иневич
21

Поскольку мне не нравится видеть «HTTPContext.Current.Session» об этом месте, я использую одноэлементный шаблон для доступа к переменным сеанса, он дает вам легкий доступ к строго типизированному пакету данных.

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

    // ...

тогда вы можете получить доступ к своим данным из любого места:

SessionSingleton.Current.SessionVariable = "Hello, World!";
Dead.Rabit
источник
2
Так что у этого класса есть две обязанности: поддерживать один экземпляр и хранить переменные ... Я бы использовал контейнер IOC, чтобы иметь синглтон.
Jowen
1
Если у вас уже есть одна настройка, я бы, вероятно, также создал полноценную службу сеансов с возможностью инъекции, хотя согласованность, вероятно, самое большое преимущество, я был бы более склонен использовать этот код для небольших веб-приложений с набором функций ... веб-мастеров, если хотите.
Dead.Rabit
14

Если вы используете asp.net mvc, вот простой способ получить доступ к сеансу.

Из контроллера:

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

Из вида:

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

Это определенно не лучший способ доступа к переменным сеанса, но это прямой маршрут. Поэтому используйте его с осторожностью (желательно во время быстрого создания прототипов) и используйте Wrapper / Container и OnSessionStart, когда это станет уместным.

НТН

robertz
источник
2
хм .. Какой способ лучше? Я должен передать данные ViewState из Session на контроллере, не так ли?
RredCat
2
а не могли бы вы объяснить ограничения этого метода?
RredCat
1
Я думаю, он имел в виду, что лучше иметь методы чтения / записи. В зависимости от использования параллелизма / потока, вам также могут понадобиться блокировки в этих методах чтения / записи, чтобы избежать условия гонки.
DeepSpace101
13

Ну имхо ..

  1. никогда не ссылаться на сеанс внутри вашей страницы просмотра / главной страницы
  2. свести к минимуму использование сеанса. Для этого MVC предоставляет TempData obj, который, по сути, является сеансом, который живет в течение одной поездки на сервер.

Что касается # 1, у меня есть строго типизированный Master View, у которого есть свойство для доступа к тому, что представляет объект Session .... в моем случае, строго типизированный Master View является универсальным, что дает мне некоторую гибкость в отношении строго типизированных View Pages.

ViewMasterPage<AdminViewModel>

AdminViewModel
{
    SomeImportantObjectThatWasInSession ImportantObject
}

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

а потом...

ViewPage<AdminViewModel<U>>
E Rolnicki
источник
7

Хотя я не знаю насчет asp.net mvc, но это то, что мы должны делать на обычном веб-сайте .net. Это должно работать и для asp.net mvc.

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

Вы бы поместили это в метод для легкого доступа. НТН

Dotnet
источник
7

Есть 3 способа сделать это.

  1. Вы можете получить прямой доступ HttpContext.Current.Session

  2. Вы можете издеваться HttpContextBase

  3. Создать метод расширения для HttpContextBase

Я предпочитаю 3-й способ. Эта ссылка является хорошей ссылкой.

Методы сеанса Get / Set HttpContext в BaseController vs Mocking HttpContextBase для создания методов Get / Set

Мангеш Пимпалкар
источник
7

Мой способ доступа к сеансам - написать вспомогательный класс, который инкапсулирует различные имена полей и их типы. Я надеюсь, что этот пример помогает:

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

    }
}
Даниил
источник
Мне нравится ваш ответ ... не могли бы вы подробнее уточнить, что происходит ... и почему этот подход лучше, чем другие ответы в этой теме.
Chef_Code
6

Отличные ответы от ребят, но я бы предостерег вас от того, чтобы всегда полагаться на сессию. Это быстро и легко сделать, и, конечно, будет работать, но не будет хорошо во всех ситуациях.

Например, если вы столкнулись со сценарием, в котором ваш хостинг не позволяет использовать сеанс, или вы находитесь в веб-ферме, или в примере общего приложения SharePoint.

Если вам нужно другое решение, вы можете использовать контейнер IOC, например Castle Windsor , создать класс провайдера в качестве оболочки и затем сохранить один экземпляр вашего класса, используя индивидуальный запрос или образ жизни сеанса, в зависимости от ваших требований.

МОК будет гарантировать, что один и тот же экземпляр возвращается каждый раз.

Сложнее да, если вам нужно простое решение, просто используйте сеанс.

Вот некоторые примеры реализации ниже из интереса.

Используя этот метод, вы можете создать класс провайдера в соответствии с:

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

    public string CustomClass { get; private set; }
}

И зарегистрируйте это примерно так:

public void Install(IWindsorContainer container, IConfigurationStore store)
{
    container.Register(
            Component.For<ICustomClassProvider>().UsingFactoryMethod(
                () => new CustomClassProvider(new CustomClass())).LifestylePerWebRequest());
    }
shenku
источник
4

Вы можете использовать ViewModelBase в качестве базового класса для всех моделей, этот класс будет заботиться о получении данных из сессии

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

Вы можете написать метод расширения в HttpContextBase для работы с данными сеанса

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

Используйте это как ниже в контроллере

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

Второй аргумент является необязательным, он будет использоваться для заполнения данных сеанса для этого ключа, когда значение не присутствует в сеансе.

Аджай Келкар
источник