Лучший способ делать строго типизированные сессии ASP.NET MVC - PullRequest
20 голосов
/ 10 ноября 2009

Я занимаюсь разработкой проекта 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?

Ответы [ 5 ]

18 голосов
/ 10 ноября 2009

Таким образом, другие объекты не будут иметь доступа к этому объекту (например, 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 )) внутри ActionFilter.

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

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

5 голосов
/ 05 февраля 2013

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

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

}
2 голосов
/ 26 июля 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 просто не реализует это как стандарт, ускользает от меня.

1 голос
/ 19 января 2017

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

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

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

1 голос
/ 28 июля 2013

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

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

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

    /// <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% безопасной для контекста запроса.

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

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