C #: Итак, если статический класс - это плохая практика для хранения информации о глобальном состоянии, что является хорошей альтернативой, которая предлагает такое же удобство? - PullRequest
25 голосов
/ 12 августа 2009

Я заметил, что статические классы получают много плохих повторений на SO в связи с тем, что они используются для хранения глобальной информации. (И вообще, над глобальными переменными пренебрегают) Я просто хотел бы знать, что является хорошей альтернативой для моего примера ниже ...

Я разрабатываю приложение WPF, и многие представления данных, извлеченных из моей базы данных, фильтруются на основе идентификатора текущего вошедшего в систему пользователя. Аналогичным образом, определенные пункты в моем приложении должны быть доступны только пользователям, которые считаются «администраторами».

В настоящее время я храню loggedInUserId и isAdmin bool в статическом классе.

Различные части моего приложения нуждаются в этой информации, и мне интересно, почему это не идеально в этом случае, и каковы альтернативы. Кажется, очень удобно начать работать.

Единственная вещь, о которой я могу подумать как об альтернативе, - это использовать IoC-контейнер для внедрения экземпляра Singleton в классы, которым нужна эта глобальная информация, и классы могли бы об этом говорить через свой интерфейс. Тем не менее, это перебор / приводит меня к анализу паралич?

Заранее благодарим за понимание.


Обновление

Так что я склоняюсь к внедрению зависимостей через IoC, так как это лучше поддается тестируемости, так как я мог бы поменяться в сервисе, который предоставляет "глобальную" информацию с макетом, если это необходимо. Я полагаю, что остается то, должен ли вводимый объект быть одноэлементным или статическим. : -)

Вероятно, выберет ответ Марка, ожидая, если будет еще обсуждение. Я не думаю, что есть правильный путь как таковой. Мне просто интересно посмотреть на обсуждение, которое бы меня просветило, так как кажется, что по некоторым схожим вопросам много «это плохо», «плохо», без каких-либо конструктивных альтернатив.


Обновление № 2 Поэтому я выбрал ответ Роберта, видя, что это отличная альтернатива (я полагаю, альтернатива - это странное слово, вероятно, Единый истинный путь, видящий, как он встроен в структуру). Это не заставляет меня создавать статический класс / синглтон (хотя это статический поток).

Единственное, что по-прежнему вызывает у меня любопытство, - как бы это было решено, если бы "глобальные" данные, которые я должен был хранить, не имели никакого отношения к аутентификации пользователя.

Ответы [ 3 ]

12 голосов
/ 12 августа 2009

Забудьте синглетоны и статические данные. Этот шаблон доступа когда-нибудь подведет вас.

Создайте свой собственный 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 (Depenancy Injection), внедряя службу членства, которая получает / проверяет учетные данные.
  • вы можете использовать шаблон Repository, чтобы также получить доступ к текущему MyAppUser (хотя, возможно, это просто приведение к MyAppUser для вас, в этом могут быть преимущества)
2 голосов
/ 12 августа 2009

Я бы попробовал другой подход. Класс статических данных доставит вам неприятности - это из опыта. Вы можете иметь объект User (см. Ответ @Robert Paulson, чтобы узнать, как это сделать) и передать его каждому объекту при его создании - он может работать для вас, но вы получите много шаблонных кодов, которые просто повторяются везде .

Вы можете хранить все свои объекты в базе данных / зашифрованном файле с необходимыми разрешениями, а затем динамически загружать все из них в соответствии с разрешениями пользователей. С простой формой администратора в базе данных ее довольно легко поддерживать (файл немного сложнее).

Вы можете создать объект requireAdminPermissionAttribute, который будет применяться ко всем вашим чувствительным объектам и проверять его во время выполнения в отношении вашего объекта User для условной загрузки в объекты.

Хотя маршрут, на котором вы сейчас находитесь, имеет свои достоинства, я думаю, что есть несколько лучших вариантов, чтобы попробовать.

2 голосов
/ 12 августа 2009

Есть много других ответов на SO, которые объясняют, почему статика (включая Singleton) вредна для вас, поэтому я не буду вдаваться в подробности (хотя я искренне поддерживаю эти чувства).

Как правило, DI - это путь. Затем вы можете внедрить службу, которая может сообщить вам все, что вам нужно знать об окружающей среде.

Однако, поскольку вы имеете дело с пользовательской информацией, Thread.CurrentPrincipal может быть жизнеспособной альтернативой (хотя является Статическим потоком).

Для удобства вы можете обернуть вокруг него строго типизированный класс User .

...