【问题标题】:How to make Singleton in MVC 5 session specific?如何使 MVC 5 会话中的单例具体化?
【发布时间】:2015-08-26 23:23:51
【问题描述】:

我的 MVC 应用程序中有一个 Singleton 模型类,用于确定登录用户是否具有授权/管理员权限(基于某些 AD 组的成员资格)。这个模型类需要是一个 Singleton 以便用户的访问权限可以在第一次登录时建立一次并在整个会话中使用:

public sealed class ApplicationUser
{
    // SINGLETON IMPLEMENTATION
    // from http://csharpindepth.com/articles/general/singleton.aspx#lazy
    public static ApplicationUser CurrentUser { get { return lazy.Value; } }

    private static readonly Lazy<ApplicationUser> lazy = 
        new Lazy<ApplicationUser>(() => new ApplicationUser());

    private ApplicationUser()
    {
        GetUserDetails(); // determine if user is authorized/admin 
    }

    // Public members
    public string Name { get { return name; } }
    public bool IsAuthorized { get { return isAuthorized; } }
    public bool IsAdmin { get { return isAdmin; } }

    // Private members
    // more code
}

Singleton 第一次在我的所有其他控制器派生自的 EntryPointController 中实例化:

public abstract class EntryPointController : Controller
{
    // this is where the ApplicationUser class in instantiated for the first time
    protected ApplicationUser currentUser = ApplicationUser.CurrentUser;        
    // more code
    // all other controllers derive from this
}

这种模式允许我在整个应用程序中使用ApplicationUser.CurrentUser.NameApplicationUser.CurrentUser.IsAuthorized 等。

但是,问题是这样的:
Singleton 包含在 Web 应用程序启动时登录的第一个用户的引用!所有后续登录的用户都会看到最早登录用户的名称!

如何使单例会话具体化?

【问题讨论】:

    标签: c# asp.net-mvc asp.net-mvc-5 singleton


    【解决方案1】:

    我认为您正在寻找 Multiton 模式,其中每个实例都链接到一个键。

    这里的一个例子

    http://designpatternsindotnet.blogspot.ie/2012/07/multiton.html

    using System.Collections.Generic;
    using System.Linq;
    
    namespace DesignPatterns
    {
        public class Multiton
        {
            //read-only dictionary to track multitons
            private static IDictionary<int, Multiton> _Tracker = new Dictionary<int, Multiton> { };
    
            private Multiton()
            {
            }
    
            public static Multiton GetInstance(int key)
            {
                //value to return
                Multiton item = null;
    
                //lock collection to prevent changes during operation
                lock (_Tracker)
                { 
                    //if value not found, create and add
                    if(!_Tracker.TryGetValue(key, out item))
                    {
                        item = new Multiton();
    
                        //calculate next key
                        int newIdent = _Tracker.Keys.Max() + 1;
    
                        //add item
                        _Tracker.Add(newIdent, item);
                    }
                }
                return item;
            }
        }
    }
    

    【讨论】:

    • 谢谢,您的指点帮助了!我已经发布了我的工作解决方案。
    【解决方案2】:

    我使用混合 Singleton-Multiton 方法得到了它(感谢 @Kickaha 的 Multiton 指针)。

    public sealed class ApplicationUser
    {
        // SINGLETON-LIKE REFERENCE TO CURRENT USER ONLY
    
        public static ApplicationUser CurrentUser
        { 
            get 
            { 
                return GetUser(HttpContext.Current.User.Identity.Name); 
            } 
        }
    
        // MULTITON IMPLEMENTATION (based on http://stackoverflow.com/a/32238734/979621)
    
        private static Dictionary<string, ApplicationUser> applicationUsers 
                                = new Dictionary<string, ApplicationUser>();
    
        private static ApplicationUser GetUser(string username)
        {
            ApplicationUser user = null;
    
            //lock collection to prevent changes during operation
            lock (applicationUsers)
            {
                // find existing value, or create a new one and add
                if (!applicationUsers.TryGetValue(username, out user)) 
                {
                    user = new ApplicationUser();
                    applicationUsers.Add(username, user);
                }
            }
    
            return user;
        }
    
        private ApplicationUser()
        {
            GetUserDetails(); // determine current user's AD groups and access level
        }
    
        // REST OF THE CLASS CODE
    
        public string Name { get { return name; } }
        public bool IsAuthorized { get { return isAuthorized; } }
        public bool IsAdmin { get { return isAdmin; } }
    
        private string name = HttpContext.Current.User.Identity.Name;
        private bool isAuthorized = false;
        private bool isAdmin = false;
    
        // Get User details
        private void GetUserDetails()
        {
            // Check user's AD groups and determine isAuthorized and isAdmin
        }
    }
    

    我的模型和控制器没有变化。

    当前用户的对象在EntryPointController中被实例化:

    public abstract class EntryPointController : Controller
    {
        // this is where the ApplicationUser class in instantiated for the first time
        protected ApplicationUser currentUser = ApplicationUser.CurrentUser;        
        // more code
        // all other controllers derive from this
    }
    

    在我的模型和其他任何地方,我可以使用ApplicationUser.CurrentUser.NameApplicationUser.CurrentUser.IsAuthorized 等访问当前用户的属性。

    【讨论】:

    • 注销后如何从 applicationUsers 对象中删除用户。否则它将在 applicationUsers 对象中保持可用。
    • @Oasis: 好收获! 'SNag' 你能解决'Oasis' 所指的问题吗?我也在研究类似的解决方案......
    【解决方案3】:

    如何使单例会话具体化?

    下面会导致你的问题。

    Singleton 持有第一个登录用户的引用 在 Web 应用程序启动时!所有后续登录的用户 查看最早登录用户的名称!

    我认为您只需将 ApplicationUser 对象存储在每个用户的会话中。

    机制应该是这样的:

    1. 为每个经过身份验证的用户创建一个 ApplicationUser 实例。
    2. 使用密钥将ApplicationUser 实例存储在会话中。 (不必担心每个用户使用相同的密钥,因为 ASP.NET HttpSessionState 会为您处理。)
    3. 如果您想访问每个用户的 ApplicationUser 对象,只需从 HttpSessionState 获取即可。
    4. 您可以选择在 Session_OnStart 或基本控制器中创建/重新创建会话。
    5. 设置您的session 设置是否要过期。

    我希望这个解决方案对您有意义。 :)

    【讨论】:

    • 谢谢!我已经能够在不依赖 Session 对象的情况下做到这一点。请参阅我的工作解决方案。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-11-21
    • 1970-01-01
    • 2016-03-18
    • 2011-07-15
    • 2010-11-21
    • 2015-03-25
    相关资源
    最近更新 更多