2009-11-10 14 views
19

Ich entwickle ein ASP.NET MVC-Projekt und möchte stark typisierte Sitzungsobjekte verwenden. Ich habe die folgende Controller-abgeleitete Klasse implementiert diese Aufgabe zu entlarven:Bessere Art der Durchführung stark typisierten ASP.NET MVC-Sitzungen

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

} 

Dies ermöglicht es mir ein Sitzungsobjekt für jeden Controller zu definieren, die mit dem Konzept der Steuerung Isolation entspricht. Gibt es einen besseren/mehr "richtigen" Weg, vielleicht etwas, das offiziell von Microsoft unterstützt wird?

+0

Was passiert, wenn Sie den gleichen Typ an mehr als einen Controller übergeben? Eine Sitzung überschreibt die andere? –

+0

Nein, sie haben beide den gleichen Typnamen und somit den gleichen Sitzungsschlüssel. Die Sitzungsobjekte werden nicht ersetzt, sie sind nur das gleiche Objekt auf beiden Controllern. –

+0

Die unten hinzugefügte Antwort, die keine Basis-Controller erfordert und auch auf die Sitzung im Ansichtscode zugreifen kann. – Gats

Antwort

18

Auf diese Weise haben andere Objekte keinen Zugriff auf dieses Objekt (z. B. ActionFilter). Ich mache es wie folgt aus:

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

Dann kann ich entweder injizieren IUserDataStorage in den Controller Konstruktor, oder verwenden Sie ServiceLocator.Current.GetInstance (typeof (IUserDataStorage <T>)) innerhalb Action.

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

Natürlich für Fälle, in denen alle Controller benötigen diese (zum Beispiel ICurrentUser) Sie können statt dessen Eigenschaft Injektion verwenden.

+0

Ich mag das wesentlich mehr. Ich habe HttpContext vergessen. –

+0

Wie funktioniert das, wenn Sie die Einheit für die Abhängigkeitsinjektion verwenden? Der aktuelle Typ Main.Services.IUserDataStorage "1 [sipObjects.HandyGuy"] ist eine Schnittstelle und kann nicht erstellt werden. Vermissen Sie ein Typ-Mapping? – HaBo

+0

Etwas gefunden hier http://msdn.microsoft.com/en-us/library/ff660936(v=pandp.20).aspx – HaBo

4

Dies könnte besser sein für das, was Sie wollen. Ich würde nur eine Erweiterungsmethode erstellen, die auf Ihre Sitzung zugreifen kann. Der zusätzliche Vorteil der Erweiterungsmethode besteht darin, dass Sie nicht mehr von einem Controller erben müssen oder eine Abhängigkeit injizieren müssen, die wirklich nicht notwendig ist.

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

Das ist nett. +1 – Darbio

2

http://codingsmith.co.za/a-better-way-of-working-with-httpcontext-session-in-mvc/ (Entschuldigungen für die Farben auf meinem Blog war Ausrüstung um mit Themen und haben es nur noch nicht fest)

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

i mit, dass vor ein paar Jahren kam und es funktioniert gut. gleiche Grundidee wie alle anderen ich denke, warum Microsoft dies nicht einfach als Standard implementieren entzieht sich mir.

0

Ich verwende dies im Allgemeinen für einen Sitzungsschlüssel und fügen Sie dann explizit Objekte bei Bedarf hinzu. Der Grund dafür ist, dass es eine saubere Methode ist, und ich finde, dass Sie die Anzahl der Objekte in der Sitzung auf ein Minimum beschränken möchten.

Dieser spezielle Ansatz verbindet Formularauthentifizierung und Benutzersitzung an einem Ort, so dass Sie Objekte hinzufügen und vergessen können. Das Argument könnte gemacht werden, dass es ein großer Verbose ist, aber es verhindert jede Verdoppelung und Sie sollten nicht zu viele Objekte in der Sitzung haben.

Die folgenden können in einer Core-Bibliothek oder wo auch immer Sie wollen.

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

} 

Dann implementieren diese in der folgenden Klasse an die Wurzel des Projekts Namensraum (Ich habe es in der Regel in einem Code-Ordner auf mvc Projekte):

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

ENDLICH Der große Vorteil der ausdrücklich erklärt Was Sie in der Sitzung wollen, ist, dass Sie dies absolut überall in Ihrer MVC-Anwendung einschließlich der Ansichten verweisen können. Referenz Sie es einfach mit:

CurrentSession.Instance.Object1 
CurrentSession.Instance.CurrentUser 

Wieder etwas weniger generisch als andere Ansätze, aber wirklich, wirklich klar, was los ist, keine andere Ausrüstung oder Dependency Injection und 100% sicher auf den Anforderungskontext.

Eine andere Anmerkung, die dicionary Ansätze sind cool, aber Sie immer noch mit Strings überall auf Referenz Zeug. Sie könnten es mit Enums oder etwas riggen, aber ich bevorzuge das starke Tippen und Setzen und Vergessen des obigen Ansatzes.

0

Ja, es ist Jahre, nachdem diese Frage gestellt wurde und es gibt andere Wege, dies zu tun ... aber für den Fall, dass jemand auf der Suche nach etwas auftaucht, das die oben genannten Ansätze zu einem ansprechenden One-Stop-Shop kombiniert appellierte an mein Team und ich ...) Hier ist, was wir verwenden.

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

Dann können Sie in Ihren Controllern Ihre Sache mit Ihren Sitzungsvariablen stärker typisiert machen.

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

Hoffe, das hilft jemandem!

Verwandte Themen