27

我一直注意到静态类在用于存储全局信息方面在 SO 上有很多不好的代表。(而且全局变量一般都被轻视)我只想知道下面我的例子有什么好的选择......

我正在开发一个 WPF 应用程序,从我的数据库中检索到的数据的许多视图都是根据当前登录用户的 ID 进行过滤的。同样,我的应用程序中的某些点应该仅供被视为“管理员”的用户访问。

我目前在静态类中存储了一个loggedInUserId和一个isAdmin bool。

我的应用程序的各个部分都需要此信息,我想知道为什么在这种情况下它并不理想,以及替代方案是什么。启动和运行似乎非常方便。

我唯一能想到的替代方法是使用 IoC 容器将 Singleton 实例注入需要此全局信息的类中,然后这些类可以通过其接口与之对话。但是,这是否矫枉过正/导致我陷入分析瘫痪?

提前感谢您的任何见解。


更新

所以我倾向于通过 IoC 进行依赖注入,因为它可以更好地提高可测试性,因为如果需要,我可以交换一个提供“全局”信息的服务和一个模拟。我想剩下的就是注入的对象应该是单例还是静态的。:-)

会选择马克的答案,虽然在等着看是否有更多的讨论。我不认为有这样的正确方法。我只是有兴趣看到一些可以启发我的讨论,因为在一些类似问题上似乎有很多“这很糟糕”“那很糟糕”的陈述,而没有任何建设性的替代方案。


更新#2 所以我选择了罗伯特的答案,因为它是一个很好的替代方案(我认为替代方案是一个奇怪的词,可能是框架中内置的 One True Way)。它不会强迫我创建一个静态类/单例(尽管它是线程静态的)。

唯一让我感到好奇的是,如果我必须存储的“全局”数据与用户身份验证无关,这将如何解决。

4

3 回答 3

12

忘记单例和静态数据。这种访问模式有时会让你失望。

创建您自己的自定义 IPrincipal 并在适合登录的位置用它替换 Thread.CurrentPrincipal。您通常保留对当前 IIdentity 的引用。

在用户登录的例程中,例如您已经验证了他们的凭据,将您的自定义主体附加到线程。

IIdentity currentIdentity = System.Threading.Thread.CurrentPrincipal.Identity;
System.Threading.Thread.CurrentPrincipal 
   = new MyAppUser(1234,false,currentIdentity);

在 ASP.Net 中,您还可以同时HttpContext.Current.User设置

public class MyAppUser : IPrincipal
{
   private IIdentity _identity;

   private UserId { get; private set; }
   private IsAdmin { get; private set; } // perhaps use IsInRole

   MyAppUser(userId, isAdmin, iIdentity)
   {
      if( iIdentity == null ) 
         throw new ArgumentNullException("iIdentity");
      UserId = userId;
      IsAdmin = isAdmin;
      _identity = iIdentity;          
   }

   #region IPrincipal Members
   public System.Security.Principal.IIdentity Identity
   {
      get { return _identity; }
   }

   // typically this stores a list of roles, 
   // but this conforms with the OP question
   public bool IsInRole(string role)
   {  
      if( "Admin".Equals(role) )
         return IsAdmin;     

      throw new ArgumentException("Role " + role + " is not supported");
   }
   #endregion
}

这是执行此操作的首选方式,并且它在框架中是有原因的。通过这种方式,您可以以标准方式接触用户。

如果用户是匿名(未知),我们还会添加属性,以支持混合匿名/登录身份验证场景的场景。

此外:

  • 您仍然可以通过注入检索/检查凭据的成员服务来使用 DI(依赖注入)。
  • 您还可以使用 Repository 模式来访问当前的 MyAppUser (尽管可以说它只是为您转换为 MyAppUser ,但这可能有好处)
于 2009-08-11T22:03:20.090 回答
2

我会尝试不同的方法。静态数据类会给你带来麻烦——这是经验之谈。您可以拥有一个 User 对象(有关执行此操作的好方法,请参阅@Robert Paulson 的答案)并在构造它时将其传递给每个对象——它可能对您有用,但您会得到很多模板代码,这些代码只是在任何地方重复.

您可以将所有对象存储在具有必要权限的数据库/加密文件中,然后根据您的用户权限动态加载所有对象。使用数据库上的简单管理表单,维护起来非常容易(文件有点难)。

您可以创建一个 RequiresAdminPermissionAttribute 对象以应用于所有敏感对象,并在运行时根据您的 User 对象检查它以有条件地加载到对象。

虽然你现在走的路线有优点,但我认为有一些更好的选择可以尝试。

于 2009-08-11T21:56:16.500 回答
2

SO上有许多其他答案可以解释为什么静态(包括Singleton)对您不利,因此我不会详细说明(尽管我全心全意地支持这些观点)。

作为一般规则,DI是要走的路。然后,您可以注入一个服务,该服务可以告诉您您需要了解的有关环境的任何信息。

但是,由于您正在处理用户信息,因此 Thread.CurrentPrincipal 可能是一个可行的替代方案(尽管它Thread Static)。

为方便起见,您可以围绕它包装一个强类型的 User 类

于 2009-08-11T21:14:35.400 回答