【问题标题】:Better way of doing strongly-typed ASP.NET MVC sessions执行强类型 ASP.NET MVC 会话的更好方法
【发布时间】:2010-12-15 05:11:10
【问题描述】:

我正在开发一个 ASP.NET MVC 项目并希望使用强类型会话对象。我已经实现了以下 Controller 派生类来公开这个对象:

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

}

这允许我为每个控制器定义一个会话对象,这符合控制器隔离的概念。有没有更好/更“正确”的方式,也许是微软官方支持的方式?

【问题讨论】:

  • 如果将同一个类型传递给多个控制器会发生什么?一个会话会覆盖另一个会话吗?
  • 不,它们都将具有相同的类型名称,因此具有相同的会话密钥。会话对象不会被替换,它们只是两个控制器中的同一个对象。
  • 下面添加的答案不需要基本控制器,甚至可以在视图代码中访问会话。
  • 你为什么使用_T?为什么不只是T
  • @ColeJohnson 没有特别的原因,至少我不记得了。

标签: c# asp.net-mvc session strong-typing


【解决方案1】:

是的,在提出这个问题多年后,还有其他方法可以做到这一点......但万一其他人出现在寻找将上述方法结合到一个有吸引力的一站式商店的东西(至少一个有吸引力的给我的团队和我...)这就是我们使用的。

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

希望这对某人有所帮助!

【讨论】:

    【解决方案2】:

    我通常将其用作会话密钥,然后根据需要显式添加对象。这样做的原因是它是一种干净的方式,我发现您希望将会话中的对象数量保持在最低限度。

    这种特殊的方法将表单身份验证和用户会话集中到一个地方,这样您就可以添加对象并忘记它。可以说它很冗长,但它确实可以防止任何重复,并且会话中不应该有太多对象。

    以下内容可以存在于核心库中或任何您想要的地方。

        /// <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% 安全。

    另一方面,字典方法很酷,但您仍然会在各处使用字符串来引用内容。您可以使用枚举或其他东西来操纵它,但我更喜欢强类型化并设置并忘记上述方法。

    【讨论】:

      【解决方案3】:

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

      我几年前就提出了这个想法,并且效果很好。我猜想和其他人一样的基本想法,为什么微软不把它作为标准来实现让我望而却步。

      【讨论】:

        【解决方案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(); 
            }
        
        }
        

        【讨论】:

          【解决方案5】:

          这样其他对象将无法访问该对象(例如 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 注入到控制器的构造函数中,或者在 ActionFilter 中使用 ServiceLocator.Current.GetInstance(typeof(IUserDataStorage))。

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

          当然,对于 所有 控制器需要此功能(例如 ICurrentUser)的情况,您可能希望改用属性注入。

          【讨论】:

          • 我更喜欢这个。我忘记了 HttpContext。
          • 使用unity进行依赖注入时如何工作当前类型Main.Services.IUserDataStorage`1[sipObjects.HandyGuy]是接口,无法构造。您是否缺少类型映射?
          • objectData.Access 在 MyController 构造函数中始终为 null。为什么会这样
          猜你喜欢
          • 1970-01-01
          • 2011-01-03
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2011-12-12
          • 1970-01-01
          • 2012-02-11
          相关资源
          最近更新 更多