简体   繁体   English

C#:所以如果静态类是存储全局状态信息的不良做法,那么提供相同方便性的好方法是什么?

[英]C# : So if a static class is bad practice for storing global state info, what's a good alternative that offers the same convenience?

I've been noticing static classes getting a lot of bad rep on SO in regards to being used to store global information. 我一直注意到静态类在用于存储全局信息方面在SO上获得了很多不好的代表。 (And global variables being scorned upon in general) I'd just like to know what a good alternative is for my example below... (而且全局变量一般都被嘲笑)我只想知道下面的例子有什么好的选择......

I'm developing a WPF app, and many views of the data retrieved from my db are filtered based on the ID of the current logged in user. 我正在开发一个WPF应用程序,根据当前登录用户的ID过滤从我的数据库中检索到的数据的许多视图。 Similarly, certain points in my app should only be accessable to users who are deemed as 'admins'. 同样,我的应用中的某些点应该只能被视为“管理员”的用户访问。

I'm currently storing a loggedInUserId and an isAdmin bool in a static class. 我目前正在静态类中存储loggedInUserIdisAdmin bool。

Various parts of my app need this info and I'm wondering why it's not ideal in this case, and what the alternatives are. 我的应用程序的各个部分需要这些信息,我想知道为什么它在这种情况下不理想,以及替代品是什么。 It seems very convienient to get up and running. 起床和跑步似乎非常方便。

The only thing I can think of as an alternative is to use an IoC Container to inject a Singleton instance into classes which need this global information, the classes could then talk to this through its interface. 我唯一可以想到的替代方法是使用IoC容器将Singleton实例注入需要此全局信息的类中,然后类可以通过其接口与之通信。 However, is this overkill / leading me into analysis paralysis? 然而,这是否过度/导致我陷入分析瘫痪?

Thanks in advance for any insight. 提前感谢任何见解。


Update 更新

So I'm leaning towards dependency injection via IoC as It would lend itself better to testability, as I could swap in a service that provides "global" info with a mock if needed. 因此,我倾向于通过IoC进行依赖注入,因为它可以更好地提供可测试性,因为我可以交换一个服务,如果需要,可以使用模拟提供“全局”信息。 I suppose what remains is whether or not the injected object should be a singleton or static. 我想剩下的是注入的对象是单身还是静态。 :-) :-)

Will prob pick Mark's answer although waiting to see if there's any more discussion. 如果等待查看是否还有其他讨论,请问问Mark的答案。 I don't think there's a right way as such. 我不认为这是正确的方式。 I'm just interested to see some discussion which would enlighten me as there seems to be a lot of "this is bad" "that is bad" statements on some similar questions without any constructive alternatives. 我只是想看到一些可以启发我的讨论,因为在没有任何建设性的替代方案的情况下,似乎有很多“这是坏的”“很糟糕”的陈述在一些类似的问题上。


Update #2 So I picked Robert's answer seeing as it is a great alternative (I suppose alternative is a weird word, probably the One True Way seeing as it is built into the framework). 更新#2所以我选择了Robert的答案,因为它是一个很好的选择(我认为替代方案是一个奇怪的词,可能是One True Way看到的,因为它是内置于框架中)。 It's not forcing me to create a static class/singleton (although it is thread static). 它并没有强迫我创建一个静态类/单例(尽管它是线程静态的)。

The only thing that still makes me curious is how this would have been tackled if the "global" data I had to store had nothing to do with User Authentication. 唯一让我感到好奇的是,如果我必须存储的“全局”数据与用户身份验证无关,那将如何处理。

Forget Singletons and static data. 忘记单身人士和静态数据。 That pattern of access is going to fail you at some time. 这种访问模式在某个时候会让你失望。

Create your own custom IPrincipal and replace Thread.CurrentPrincipal with it at a point where login is appropriate. 创建自己的自定义IPrincipal并在适当的登录点替换Thread.CurrentPrincipal。 You typically keep the reference to the current IIdentity. 您通常会保留对当前IIdentity的引用。

In your routine where the user logs on, eg you have verified their credentials, attach your custom principal to the Thread. 在用户登录的例程中,例如,您已验证其凭据,请将自定义主体附加到线程。

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

in ASP.Net you would also set the HttpContext.Current.User at the same time 在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
}

This is the preferred way to do it, and it's in the framework for a reason. 这是执行此操作的首选方式,并且它在框架中是有原因的。 This way you can get at the user in a standard way. 这样您就可以以标准方式访问用户。

We also do things like add properties if the user is anonymous (unknown) to support a scenario of mixed anonymous/logged-in authentication scenarios. 如果用户是匿名的(未知)以支持混合匿名/登录身份验证方案的场景,我们还会执行添加属性之类的操作。

Additionally: 另外:

  • you can still use DI (Dependancy Injection) by injecting the Membership Service that retrieves / checks credentials. 您仍然可以通过注入检索/检查凭据的成员身份服务来使用DI(依赖注入)。
  • you can use the Repository pattern to also gain access to the current MyAppUser (although arguably it's just making the cast to MyAppUser for you, there can be benefits to this) 您可以使用存储库模式来获取对当前MyAppUser的访问权限(虽然可以说它只是为您投射到MyAppUser,但这可能会带来好处)

There are many other answers here on SO that explains why statics (including Singleton) is bad for you, so I will not go into details (although I wholeheartedly second those sentiments). 这里还有许多其他的答案可以解释为什么静力学(包括辛格尔顿)对你不利,所以我不会详细介绍(尽管我全心全意地回应这些情绪)。

As a general rule, DI is the way to go. 作为一般规则,DI是要走的路。 You can then inject a service that can tell you anything you need to know about the environment. 然后,您可以注入一项服务,告诉您需要了解的有关环境的任何信息。

However, since you are dealing with user information, Thread.CurrentPrincipal may be a viable alternative (although it is Thread Static). 但是,由于您正在处理用户信息,Thread.CurrentPrincipal可能是一个可行的替代方案(尽管它 Thread Static)。

For convenience, you can wrap a strongly typed User class around it . 为方便起见,您可以围绕它包装强类型的User类

I'd try a different approach. 我会尝试不同的方法。 The static data class is going to get you in trouble -- this is from experience. 静态数据类会让你遇到麻烦 - 这来自经验。 You could have a User object (see @Robert Paulson's answer for a great way to do this) and pass that to every object as you construct it -- it might work for you but you'll get a lot template code that just repeats everywhere. 你可以拥有一个User对象(请参阅@Robert Paulson的答案以获得一个很好的方法)并在构建它时将其传递给每个对象 - 它可能对你有用,但你会得到很多模板代码,只能在任何地方重复。

You could store all your objects in a database / encrypted file with the necessary permissions and then dynamically load all of them based on your Users permissions. 您可以使用必要的权限将所有对象存储在数据库/加密文件中,然后根据您的用户权限动态加载所有对象。 With a simple admin form on the database, it's pretty easy to maintain (the file is a little bit harder). 使用数据库上的简单管理表单,它很容易维护(文件有点难)。

You could create a RequiresAdminPermissionAttribute object to apply to all your sensitive objects and check it at run-time against your User object to conditionally load to objects. 您可以创建RequiresAdminPermissionAttribute对象以应用于所有敏感对象,并在运行时针对User对象检查它以有条件地加载到对象。

While the route you're on now has merit, I think there are some better options to try. 虽然你现在的路线有优点,但我认为还有一些更好的选择。

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

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM