做强类型ASP.NET MVC会话的更好方法

时间:2009-11-10 20:14:23

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

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

}

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

5 个答案:

答案 0 :(得分: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&lt; T&gt;))。

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

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

答案 1 :(得分: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(); 
    }

}

答案 2 :(得分: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;
   }
}
几年前,我提出了这个问题并且工作正常。和其他人一样基本的想法,我想,为什么微软不会只是实现这个标准逃避我。

答案 3 :(得分:1)

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

这种特殊方法将表单身份验证和用户会话集中到一个位置,因此您可以添加对象并忘记它。可以说它是一个很大的冗长,但确实可以防止任何加倍,你不应该在会话中有太多的对象。

以下内容可以存在于核心库中,也可以存在于任何位置。

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

另一方面,dicionary方法很酷,但你仍然会在各处找到字符串以引用内容。您可以使用枚举或其他东西进行装配,但我更喜欢强类型和设置而忘记上述方法。

答案 4 :(得分: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);
}

希望这有助于某人!