简体   繁体   English

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

[英]Better way of doing strongly-typed ASP.NET MVC sessions

I am developing an ASP.NET MVC project and want to use strongly-typed session objects. 我正在开发一个ASP.NET MVC项目,并希望使用强类型的会话对象。 I have implemented the following Controller-derived class to expose this object: 我已实现以下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];
        }
    }

}

This allows me to define a session object for each controller, which is in line with the concept of controller isolation. 这允许我为每个控制器定义一个会话对象,这符合控制器隔离的概念。 Is there a better/more "correct" way, perhaps something that is officially supported by Microsoft? 是否有更好/更“正确”的方式,也许是微软官方支持的方式?

This way other objects won't have access to this object (eg ActionFilter). 这样,其他对象将无法访问此对象(例如ActionFilter)。 I do it like this: 我是这样做的:

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

Then, I can either inject IUserDataStorage into controller's constructor, or use ServiceLocator.Current.GetInstance(typeof(IUserDataStorage<T>)) inside ActionFilter. 然后,我可以将IUserDataStorage注入到控制器的构造函数中,或者在ActionFilter中使用ServiceLocator.Current.GetInstance(typeof(IUserDataStorage <T>))。

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

Of course for cases when all controllers need this (eg ICurrentUser) you may want to use property injection instead. 当然,对于所有控制器都需要这种情况的情况(例如ICurrentUser),您可能希望使用属性注入。

This might be better for what you want. 这可能对你想要的更好。 I would just create an extension method that can access your session. 我只想创建一个可以访问会话的扩展方法。 The added benefit to the extension method is that you no longer have to inherit from a controller, or have to inject a dependency that really isn't necessary to begin with. 扩展方法的附加好处是您不再需要从控制器继承,或者必须注入一个真正不必开始的依赖项。

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

}

http://codingsmith.co.za/a-better-way-of-working-with-httpcontext-session-in-mvc/ (apologies for the colours on my blog was tooling around with themes and just havent fixed it yet) 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;
   }
}

i came up with that a few years ago and it works fine. 几年前我想出了它,它工作正常。 same basic idea as everyone else i guess, why microsoft dont just implement this as standard eludes me. 和其他人一样基本的想法,我想,为什么微软不会只是实现这个标准逃避我。

I generally use this for a session key and then explicitly add objects as needed. 我通常将此用作会话密钥,然后根据需要显式添加对象。 The reason for this is it's a clean way to do it and I find that you want to keep the number of objects in session to a minimum. 这样做的原因是它是一种干净的方式,我发现你想要将会话中的对象数量保持在最低限度。

This particular approach brings together forms authentication and user session into one place so you can add objects and forget about it. 这种特殊的方法将表单身份验证和用户会话集中到一个地方,因此您可以添加对象并忘记它。 The argument could be made that it is a big verbose, but it does prevent any double up and you shouldn't have too many objects in session. 可以说它是一个很大的冗长,但它确实可以防止任何加倍,你不应该在会话中有太多的对象。

The following can exist in a core library or wherever you want. 以下内容可以存在于核心库中,也可以存在于任何位置。

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

}

Then implement this in the following class namespaced to the root of your project (I usually put it in a code folder on mvc projects): 然后在命名为项目根目录的以下类中实现它(我通常把它放在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 The big advantage of explicitly declaring what you want in session is that you can reference this absolutely anywhere in your mvc application including the views. 最后在会话中明确声明你想要的东西的最大优点是你可以在你的mvc应用程序中的任何地方引用它,包括视图。 Just reference it with: 只需参考:

CurrentSession.Instance.Object1
CurrentSession.Instance.CurrentUser

Again a little less generic than other approaches, but really really clear what's going on, no other rigging or dependancy injection and 100% safe to the request context. 再次比其他方法更不通用,但真正清楚发生了什么,没有其他索具或依赖注入,并且对请求上下文100%安全。

On another note, the dicionary approaches are cool, but you still end up with strings all over the place to reference stuff. 另一方面,dicionary方法很酷,但是你仍然会在整个地方使用字符串来引用内容。 You could rig it with enums or something, but I prefer the strong typing and set and forget of the above approach. 你可以用枚举或其他东西来装配它,但我更喜欢强大的打字和设置而忘记了上述方法。

Yes, it's years after this question was asked and there are other ways to do this... but in case anyone else shows up looking for something that combines the approaches above into an appealing one stop shop (at least one that appealed to my team and I...) Here's what we use. 是的,在问这个问题之后的几年,还有其他方法可以做到这一点......但是如果有其他人出现寻找将上述方法结合到一个吸引人的一站式商店的事情(至少有一个吸引我的团队而我...)这是我们使用的。

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

Then in your controllers you can do your thing with your session variables in a more strongly typed way. 然后在您的控制器中,您可以使用更强类型的方式对会话变量执行操作。

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

Hope this helps someone! 希望这有助于某人!

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 绑定到ASP.NET MVC中的强类型对象的集合 - Binding to a Collection of Strongly-Typed Objects in ASP.NET MVC 带有实体框架的强类型ASP.NET MVC - Strongly-Typed ASP.NET MVC with Entity Framework ASP.Net Core MVC 如何针对强类型模型发布未知数量的字段 - ASP.Net Core MVC how to post unknown number of fields against strongly-typed model 从asp.net mvc 3格式的强类型视图中收集数据 - Collect data from a strongly-typed view in asp.net mvc 3 form 是否可以在ASP.NET MVC中创建强类型的复杂视图 - Is it possible to create a strongly-typed complex view in ASP.NET MVC ASP.Net MVC 2在为强类型视图自动生成视图内容时省略属性 - ASP.Net MVC 2 Omitting Properties When Autogenerating View Content For Strongly-Typed View ASP.NET强类型全球化资源值 - ASP.NET Strongly-typed globalization resource values 带有复杂ItemType的ASP.NET强类型DataControl(中继器)失败 - ASP.NET Strongly-typed DataControl (Repeater) with complex ItemType failing 来自appsettings.json的ASP.NET Core强类型选项 - ASP.NET Core strongly-typed options from appsettings.json 无法使用 Asp.Net Core 3 中的命名选项读取/绑定具有强类型 class 的配置设置 - Unable to read/bind configuration settings with strongly-typed class using Named Options in Asp.Net Core 3
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM