21

我正在开发一个 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];
        }
    }

}

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

4

5 回答 5

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 注入到控制器的构造函数中,或者在 ActionFilter 中使用 ServiceLocator.Current.GetInstance(typeof(IUserDataStorage<T>))。

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

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

于 2009-11-10T20:55:17.583 回答
5

这可能对你想要的更好。我只想创建一个可以访问您的会话的扩展方法。扩展方法的额外好处是您不再需要从控制器继承,或者必须注入一个开始时确实不需要的依赖项。

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

}
于 2013-02-05T18:33:32.727 回答
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;
   }
}

几年前我想出了这个,它工作正常。我猜想和其他人一样的基本想法,为什么微软不把它作为标准来实现让我望而却步。

于 2013-07-26T09:28:11.130 回答
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% 安全。

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

于 2013-07-28T07:01:15.227 回答
2

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

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

希望这对某人有帮助!

于 2017-01-19T17:34:06.337 回答