Лучший способ делать строго типизированные сеансы ASP.NET MVC

Я разрабатываю проект ASP.NET MVC и хочу использовать строго типизированные объекты сеанса. Я реализовал следующий класс, производный от контроллера, чтобы раскрыть этот объект:

public class StrongController<_T> : Controller
    where _T : new()
{
    public _T SessionObject
    {
        get
        {
            if (Session[typeof(_T).FullName] == null)
            {
                _T newsession = new _T();
                Session[typeof(_T).FullName] = newsession;
                return newsession;
            }
            else
                return (_T)Session[typeof(_T).FullName];
        }
    }

}

Это позволяет мне определять объект сеанса для каждого контроллера, что соответствует концепции изоляции контроллера. Есть ли лучший / более «правильный» способ, возможно, официально поддерживаемый Microsoft?


person David Pfeffer    schedule 10.11.2009    source источник
comment
Что произойдет, если вы передадите один и тот же тип более чем одному контроллеру? Один сеанс перезапишет другой?   -  person    schedule 10.11.2009
comment
Нет, у них обоих будет одно и то же имя типа и, следовательно, один и тот же ключ сеанса. Объекты сеанса не будут заменены, они будут одним и тем же объектом на обоих контроллерах.   -  person David Pfeffer    schedule 10.11.2009
comment
Ответ, добавленный ниже, не требует базовых контроллеров и может даже получить доступ к сеансу в коде представления.   -  person Gats    schedule 28.07.2013
comment
Почему вы используете _T? Почему не просто T?   -  person Cole Johnson    schedule 28.07.2013
comment
@ColeJohnson Нет особой причины, по крайней мере, насколько я помню.   -  person David Pfeffer    schedule 29.07.2013


Ответы (5)


Таким образом, другие объекты не будут иметь доступа к этому объекту (например, ActionFilter). Я так делаю:

public interface IUserDataStorage<T>
{
   T Access { get; set; }
}

public class HttpUserDataStorage<T>: IUserDataStorage<T>
  where T : class
{
  public T Access
  {
     get { return HttpContext.Current.Session[typeof(T).FullName] as T; }
     set { HttpContext.Current.Session[typeof(T).FullName] = value; }
  }
}

Затем я могу либо ввести IUserDataStorage в конструктор контроллера, либо использовать ServiceLocator.Current.GetInstance (typeof (IUserDataStorage ‹T›)) внутри ActionFilter.

public class MyController: Controller
{
   // automatically passed by IoC container
   public MyController(IUserDataStorage<MyObject> objectData)
   {
   }
}

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

person queen3    schedule 10.11.2009
comment
Мне это нравится существенно больше. Я забыл про HttpContext. - person David Pfeffer; 11.11.2009
comment
Как это работает при использовании единства для внедрения зависимости Текущий тип Main.Services.IUserDataStorage`1 [sipObjects.HandyGuy] является интерфейсом и не может быть сконструирован. Вам не хватает сопоставления типов? - person HaBo; 17.05.2014
comment
Нашел кое-что здесь msdn.microsoft.com/en- us / library / ff660936 (v = pandp.20) .aspx - person HaBo; 18.05.2014
comment
objectData.Access всегда имеет значение null в конструкторе MyController. это почему - person HaBo; 19.05.2014

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

public static class SessionExtensions {
  public static T Get<T>(this HttpSessionBase session, string key)  {
     var result;
     if (session.TryGetValue(key, out result))
     {
        return (T)result;
     }
     // or throw an exception, whatever you want.
     return default(T);
   }
 }


public class HomeController : Controller {
    public ActionResult Index() {
       //....

       var candy = Session.Get<Candy>("chocolate");

       return View(); 
    }

}
person Khalid Abuhakmeh    schedule 05.02.2013

http://codingsmith.co.za/a-better-way-of-working-with-httpcontext-session-in-mvc/ (извинения за цвета в моем блоге были связаны с темами и просто еще не исправили это)

public interface ISessionCache
{
  T Get<T>(string key);
  void Set<T>(string key, T item);
  bool contains(string key);
  void clearKey(string key);
  T singleTon<T>(String key, getStuffAction<T> actionToPerform);
}


public class InMemorySessionCache : BaseSessionCache
{
    Dictionary<String, Object> _col;
    public InMemorySessionCache()
    {
        _col = new Dictionary<string, object>();
    }

    public T Get<T>(string key)
    {
        return (T)_col[key];
    }

    public void Set<T>(string key, T item)
    {
        _col.Add(key, item);
    }

    public bool contains(string key)
    {
        if (_col.ContainsKey(key))
        {
            return true;
        }
        return false;
    }

    public void clearKey(string key)
    {
        if (contains(key))
        {
            _col.Remove(key);
        }
    }
}



public class HttpContextSessionCache : BaseSessionCache
{
   private readonly HttpContext _context;

  public HttpContextSessionCache()
   {
      _context = HttpContext.Current;
   }

  public T Get<T>(string key)
   {
      object value = _context.Session[key];
      return value == null ? default(T) : (T)value;
   }

  public void Set<T>(string key, T item)
   {
      _context.Session[key] = item;
   }

   public bool contains(string key)
   {
       if (_context.Session[key] != null)
       {
          return true;
       }
       return false;
   }
   public void clearKey(string key)
   {
       _context.Session[key] = null;
   }
}

Я придумал это несколько лет назад, и он отлично работает. та же основная идея, что и все, я думаю, почему Microsoft не реализует это как стандарт, ускользает от меня.

person spaceman    schedule 26.07.2013

Да, прошли годы после того, как этот вопрос был задан, и есть другие способы сделать это ... но в случае, если кто-то еще появится в поисках чего-то, что объединяет вышеперечисленные подходы в привлекательный универсальный магазин (по крайней мере, тот, который понравился моей команде и я ...) Вот что мы используем.

 public enum SessionKey { CurrentUser, CurrentMember, CurrentChart, CurrentAPIToken, MemberBanner }

 public static class SessionCache {

    public static T Get<T>(this HttpSessionStateBase session, SessionKey key)
    {
        var value = session[key.ToString()];
        return value == null ? default(T) : (T) value;
    }

    public static void Set<T>(this HttpSessionStateBase session, SessionKey key, T item)
    {
        session[key.ToString()] = item;
    }

    public static bool contains(this HttpSessionStateBase session, SessionKey key)
    {
        if (session[key.ToString()] != null)
            return true;
        return false;
    }

    public static void clearKey(this HttpSessionStateBase session, SessionKey key)
    {
        session[key.ToString()] = null;
    }
}

Затем в ваших контроллерах вы можете делать то, что вам нужно, с переменными сеанса более строго типизированным способом.

// get member
var currentMember = Session.Get<Member>(SessionKey.CurrentMember);
// set member
Session.Set<Member>(SessionKey.CurrentMember, currentMember);
// clear member
Session.ClearKey(SessionKey.CurrentMember);
// get member if in session
if (Session.Contains(SessionKey.CurrentMember))
{
     var current = Session.Get<Member>(SessionKey.CurrentMember);
}

Надеюсь, это кому-то поможет!

person Mike Devenney    schedule 19.01.2017

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

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

Следующее может существовать в основной библиотеке или где угодно.

    /// <summary>
    /// Provides a default pattern to access the current user in the session, identified
    /// by forms authentication.
    /// </summary>
    public abstract class MySession<T> where T : class
    {
        public const string USERSESSIONKEY = "CurrentUser";

        /// <summary>
        /// Gets the object associated with the CurrentUser from the session.
        /// </summary>
        public T CurrentUser
        {
            get
            {
                if (HttpContext.Current.Request.IsAuthenticated)
                {
                    if (HttpContext.Current.Session[USERSESSIONKEY] == null)
                    {
                        HttpContext.Current.Session[USERSESSIONKEY] = LoadCurrentUser(HttpContext.Current.User.Identity.Name);
                    }
                    return HttpContext.Current.Session[USERSESSIONKEY] as T;
                }
                else
                {
                    return null;
                }
            }
        }

        public void LogOutCurrentUser()
        {
            HttpContext.Current.Session[USERSESSIONKEY] = null;
            FormsAuthentication.SignOut();
        }

        /// <summary>
        /// Implement this method to load the user object identified by username.
        /// </summary>
        /// <param name="username">The username of the object to retrieve.</param>
        /// <returns>The user object associated with the username 'username'.</returns>
        protected abstract T LoadCurrentUser(string username);
    }

}

Затем реализуйте это в следующем классе, расположенном в пространстве имен в корне вашего проекта (я обычно помещаю его в папку кода в проектах mvc):

public class CurrentSession : MySession<PublicUser>
{
    public static CurrentSession Instance = new CurrentSession();

    protected override PublicUser LoadCurrentUser(string username)
    {
        // This would be a data logic call to load a user's detail from the database
        return new PublicUser(username);
    }

    // Put additional session objects here
    public const string SESSIONOBJECT1 = "CurrentObject1";
    public const string SESSIONOBJECT2 = "CurrentObject2";

    public Object1 CurrentObject1
    {
        get
        {
            if (Session[SESSIONOBJECT1] == null)
                Session[SESSIONOBJECT1] = new Object1();

            return Session[SESSIONOBJECT1] as Object1;
        }
        set
        {
            Session[SESSIONOBJECT1] = value;
        }
    }

    public Object2 CurrentObject2
    {
        get
        {
            if (Session[SESSIONOBJECT2] == null)
                Session[SESSIONOBJECT2] = new Object2();

            return Session[SESSIONOBJECT2] as Object2;
        }
        set
        {
            Session[SESSIONOBJECT2] = value;
        }
    }
}

НАКОНЕЦ Большим преимуществом явного объявления того, что вы хотите в сеансе, является то, что вы можете ссылаться на это абсолютно в любом месте вашего приложения mvc, включая представления. Просто укажите на него:

CurrentSession.Instance.Object1
CurrentSession.Instance.CurrentUser

Опять же, немного менее общий, чем другие подходы, но действительно ясно, что происходит, никаких других манипуляций или инъекций зависимостей и на 100% безопасен для контекста запроса.

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

person Gats    schedule 28.07.2013