如何使MVC 5会话中的Singleton具体化?

时间:2015-08-26 23:23:52

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

我的MVC应用程序中有一个Singleton模型类,用于确定登录的用户是否具有授权/ admin(基于某些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应用程序时登录的第一个用户的引用!登录的所有后续用户都会看到最早登录用户的名称!

如何使Singleton会话具体化?

2 个答案:

答案 0 :(得分:3)

我使用混合的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等访问当前用户的属性。

答案 1 :(得分:1)

  

如何使Singleton会话具体化?

将在下面导致您的问题。

  

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

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

机制应如下所示:

  1. 为每个经过身份验证的用户创建ApplicationUser的实例。
  2. ApplicationUser实例存储在带密钥的会话中。 (不要担心每个用户使用相同的密钥,因为ASP.NET HttpSessionState会为您处理它。)
  3. 如果您想要按用户访问ApplicationUser个对象,只需从HttpSessionState获取。
  4. 您可以选择在Session_OnStart或基本控制器中创建/重新创建会话。
  5. 如果您希望它过期,请设置session设置。
  6. 我希望这个解决方案对你有意义。 :)