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

前端 未结 3 1945
野趣味
野趣味 2020-12-24 02:13

I\'ve been noticing static classes getting a lot of bad rep on SO in regards to being used to store global information. (And global variables being scorned upon in general)

相关标签:
3条回答
  • 2020-12-24 02:29

    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. You typically keep the reference to the current IIdentity.

    In your routine where the user logs on, e.g. 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

    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.
    • 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)
    0 讨论(0)
  • 2020-12-24 02:32

    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.

    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.

    While the route you're on now has merit, I think there are some better options to try.

    0 讨论(0)
  • 2020-12-24 02:44

    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. 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).

    For convenience, you can wrap a strongly typed User class around it.

    0 讨论(0)
提交回复
热议问题