2009-11-10 13 views
19

Estoy desarrollando un proyecto ASP.NET MVC y quiero utilizar objetos de sesión fuertemente tipados. He implementado la siguiente clase controlador derivado para exponer este objeto:Mejor forma de realizar sesiones ASP.NET MVC de tipado enérgico

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

} 

Esto me permite definir un objeto de sesión para cada controlador, que está en línea con el concepto de aislamiento controlador. ¿Hay una forma mejor/más "correcta", tal vez algo que sea oficialmente compatible con Microsoft?

+0

¿Qué pasará si pasa el mismo tipo a más de un controlador? Una sesión sobrescribirá la otra? –

+0

No, ambos tendrán el mismo nombre y, por lo tanto, la misma clave de sesión. Los objetos de la sesión no serán reemplazados, solo serán el mismo objeto en ambos controladores. –

+0

Respuesta agregada a continuación que no requiere controladores base y que incluso puede acceder a la sesión en el código de vista también. – Gats

Respuesta

18

De esta forma, otros objetos no tendrán acceso a este objeto (por ejemplo, ActionFilter). Lo hago como esto:

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

Entonces, puedo o bien inyectar IUserDataStorage en el constructor del controlador, o utilizar ServiceLocator.Current.GetInstance (typeof (IUserDataStorage <T>)) en el interior ActionFilter.

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

Por supuesto, para los casos en que todos los controladores de necesitan este (por ejemplo ICurrentUser) es posible que desee utilizar la inyección de la propiedad en su lugar.

+0

Me gusta mucho más. Me olvidé de HttpContext. –

+0

Cómo funciona esto cuando se utiliza la unidad para la inyección de dependencia El tipo actual, Main.Services.IUserDataStorage'1 [sipObjects.HandyGuy], es una interfaz y no se puede construir. ¿Te estás perdiendo un mapeo de tipo? – HaBo

+0

Encontró algo aquí http://msdn.microsoft.com/en-us/library/ff660936(v=pandp.20).aspx – HaBo

4

Esto podría ser mejor para lo que desee. Solo crearía un método de extensión que pueda acceder a su sesión. El beneficio adicional del método de extensión es que ya no tiene que heredar de un controlador, o tiene que inyectar una dependencia que realmente no es necesaria para empezar.

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

Esto es bueno. +1 – Darbio

2

http://codingsmith.co.za/a-better-way-of-working-with-httpcontext-session-in-mvc/ (disculpas por los colores en mi blog fue dando vueltas con los temas y simplemente havent fijos todavía)

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

me ocurrió que hace unos años y trabaja muy bien. la misma idea básica que todos los demás, supongo, por qué microsoft no implementa esto como estándar me elude.

0

Generalmente uso esto para una clave de sesión y luego agrego objetos explícitamente según sea necesario. La razón de esto es que es una manera limpia de hacerlo y me parece que desea mantener el número de objetos en sesión al mínimo.

Este enfoque particular reúne autenticación de formularios y sesión de usuario en un solo lugar para que pueda agregar objetos y olvidarse de ellos. Se podría argumentar que es un gran verbo, pero previene cualquier duplicación y no debería haber demasiados objetos en sesión.

Lo siguiente puede existir en una biblioteca central o donde lo desee.

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

} 

Entonces implementar esto en la siguiente clase de espacio de nombres a la raíz de su proyecto (por lo general lo puso en una carpeta de código en proyectos 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; 
     } 
    } 
} 

FINALMENTE La gran ventaja de declarar de manera explícita lo que desea en la sesión es que puede hacer referencia a esto absolutamente en cualquier lugar de la aplicación mvc, incluidas las vistas. Simplemente hacer referencia a ella con:

CurrentSession.Instance.Object1 
CurrentSession.Instance.CurrentUser 

de nuevo un poco menos genérico que otros enfoques, pero muy, muy claro lo que está pasando, ningún otro aparejo o inyección de dependencias y 100% seguro para el contexto de la petición.

En otro aspecto, los enfoques dicionarios son geniales, pero todavía terminas con cadenas por todo el lugar para hacer referencia a cosas. Puede equiparlo con enumeraciones o algo así, pero prefiero la tipificación fuerte y establecer y olvidar el enfoque anterior.

0

Sí, pasaron años después de que se hizo esta pregunta y hay otras formas de hacerlo ... pero en caso de que alguien más aparezca buscando algo que combine los enfoques anteriores en una ventanilla única atractiva (al menos una que apeló a mi equipo y yo ...) Esto es lo que usamos.

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

Luego en sus controladores puede hacer sus cosas con sus variables de sesión de una manera más fuertemente tipada.

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

Espero que esto ayude a alguien!

Cuestiones relacionadas