2009-11-10 20 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]; 
     } 
    } 

} 

這使我定義會話對象爲每個控制器,其與控制器隔離的概念線。是否有更好/更「正確」的方式,可能是微軟正式支持的方式?

+0

如果將相同類型傳遞給多個控制器,會發生什麼?一個會話會覆蓋另一個會話? – 2009-11-10 20:17:02

+0

不,它們都具有相同的類型名稱,並因此具有相同的會話密鑰。會話對象不會被替換,它們將只是兩個控制器中的同一個對象。 – 2009-11-10 20:18:32

+0

下面添加的答案不需要基礎控制器,甚至可以在視圖代碼中訪問會話。 – Gats 2013-07-28 07:06:03

回答

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 <牛逼>))內ActionFilter。

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

當然病例的時候所有控制器需要這個(例如ICurrentUser),您可能希望使用屬性注入來代替。

+0

我更喜歡這個。我忘了HttpContext。 – 2009-11-10 21:09:56

+0

當你使用unity進行依賴注入時,這是如何工作的當前類型Main.Services.IUserDataStorage'1 [sipObjects.HandyGuy]是一個接口,不能被構造。你是否缺少類型映射? – HaBo 2014-05-16 20:39:16

+0

在這裏找到了一些東西http://msdn.microsoft.com/en-us/library/ff660936(v=pandp.20).aspx – HaBo 2014-05-18 02:33:07

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 2013-07-24 12:00:08

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

我與前幾年過來了,它工作正常。與其他人相同的基本想法,我猜,爲什麼微軟不會僅僅以此爲標準來實現這一點就無法實現。

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

FINALLY 的顯式聲明的一大優勢你在會話中想要的是,你可以在你的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); 
} 

希望這可以幫助別人!