2009-11-10 1 views
19

Я разрабатываю проект 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?

+0

Что произойдет, если вы передадите один и тот же тип более чем одному контроллеру? Один сеанс перезапишет другой? – 2009-11-10 20:17:02

+0

Нет, они оба будут иметь одинаковое имя типа и, таким образом, один и тот же ключ сеанса. Объекты сеанса не будут заменены, они будут только одним и тем же объектом на обоих контроллерах. –

+0

Ответ добавлен ниже, который не требует базовых контроллеров и может даже получить доступ к сеансу в виде кода. – Gats

ответ

18

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

+0

Мне это больше нравится. Я забыл о HttpContext. –

+0

Как это работает, когда вы используете единицу для инъекции зависимостей. Текущий тип Main.Services.IUserDataStorage'1 [sipObjects.HandyGuy] является интерфейсом и не может быть сконструирован. Вам не хватает картографирования типов? – HaBo

+0

Нашел что-то здесь http://msdn.microsoft.com/en-us/library/ff660936(v=pandp.20).aspx – HaBo

4

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

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

} 
+0

Это хорошо. +1 – Darbio

2

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

0

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

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

В базовой библиотеке или в любом месте, где захотите, может существовать следующее.

/// <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% гарантией безопасности в контексте запроса.

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

0

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

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

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

 Смежные вопросы

  • Нет связанных вопросов^_^