Где хранить словари в приложении с помощью Dependency Injection - PullRequest
5 голосов
/ 02 февраля 2012

У меня есть устаревший код, и у меня проблема с его восстановлением.

При запуске приложения я загружаю из WCF свойство в список App (это SL приложение) пользователей.

Тогда каждый элемент управления (для отправки электронной почты, просмотра календаря и назначения задач) использует это свойство как

(App.Current as App).Users

Теперь я пытаюсь создать модульный тест для одного из элементов управления, использующих этот список, и я застрял.

Должен ли я сделать инъекцию конструктора (я использую Unity) с App в качестве параметра? Или, может быть, ввести какой-то класс для хранения этого списка?

Ответы [ 4 ]

4 голосов
/ 09 февраля 2012

Обновлено с реализацией OP, поскольку псевдокод был неполным.

Я предлагаю создать интерфейс для всех ваших служб приложений

Внедрить IApplicationService в ваши модули.

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

Реализация OP

 public interface IApplicationService
    {
        List<User> Users{get;set;}
    }

    public class ApplicationService : IApplicationService
    {
        public List<User> Users
        {
            get { return (App.Current as App).Users; }
            set { (App.Current as App).Users = value; }
        }
    }

    public partial class MainWindow : UserControl
    {
        readonly IApplicationService _applicationService
        public MainWindow(IApplicationService applicationService)
        {
            _applicationService=applicationService;
        }
    }
2 голосов
/ 02 февраля 2012

Я бы создал класс-обертку, который будет выставлять список пользователей. В рабочем коде этот класс будет просто оберткой вокруг вашего свойства App.Current, и его можно внедрить в конструктор через Unity.

В своих модульных тестах вы можете легко смоделировать параметр App и передать его при создании новой SUT.

Что-то вроде:

public interface IUserList
{
   List<User> Users { get; }
}

public class SUT
{
   private IUserList UserList { get; set; }

   public SUT(IUserList userList)
   {
     this.UserList = userList;
   }
}

public class AppUserList : IUserList
{
   public List<User> Users
   {
      get
      {
         return ((App)App.Current).Users;
      }
   }
}
1 голос
/ 15 февраля 2012

Джимми ответит отлично, но может предоставить немного, и некоторые ошибки исправлены. Различия объясняются внизу под кодом / инструкциями:


Создать публичный интерфейс: IUserService

public interface IUserService
{       
    // Implemented functionality as methods where possible for better
    // extendability (like IoC)
    IEnumerable<User> Users();

    // Add any other user service stuff as you see fit.
    void AddUser(User user);
}

Напишите UserService, который реализует IUserService

public class UserService : IUserService
{
    // If you need DI for this service, follow the same pattern of using 
    // fields and controller injection. I left examples in comment below.

    // private readonly IRepository _repository;

    // Constructor is unnecessary if you do not need DI example.
    public UserService(/* IRepository repository */) 
    {
        // _repository = repository;
    }

    // Methods
    public IEnumerable<User> Users()
    {
        return ((App)App.Current).Users;
    }
    public void AddUser(User user)
    {
        ((App)App.Current).Users.Add(user);
    }

}

Внедрить IUserService в классы через их конструктор

В этом случае ваше MainWindow в качестве примера:

public partial class MainWindow : UserControl
{
    private readonly IUserService _userService;

    public MainWindow(IUserService userService)
    {
        _userService = userService;
    }

    // Example method consuming the service
    public IEnumerable<User> GetUsers()
    {
        return _userService.Users();
    }
}

Отличия:

  1. Отделение ваших пользовательских служб от центральной службы приложений

    Лучшая модульность. Кроме того, я использую IApplicationService для более центральных / глобальных данных, таких как ключи Api, тайм-ауты, очистка, подготовка БД и т. Д.

  2. Возврат IEnumerable<T> вместо List<T>

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

  3. Используйте методы вместо свойств

    Это предпочтение, но я думаю, что на уровне сервисов целесообразно использовать методы, где это возможно, чтобы вы могли вводить фильтры и перегрузки или продолжать использовать внедрение зависимостей - например, вы можете добавить GetUsers(string lastName), GetUsers(string lastName, string firstName) и поддерживать чистый интерфейс для ваших классов потребления.

  4. Cast App.Current без ключевого слова as

    Это хорошая практика, потому что использование ключевого слова as означает, что в случае неудачного приведения будет возвращено значение null, а не выбрасывается исключение. Я предпочитаю исключение, потому что в 99% случаев, если произойдет сбой, ваши последующие операции тоже будут. :)

Наслаждайтесь!

1 голос
/ 09 февраля 2012

Для Silverlight существует модель расширения, которая называется Служба расширения приложений .

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

Недостатком этой модели является создание синглтона, который у вас будетинициализировать для ваших юнит-тестов.Это также скрыло бы зависимость от Users в ваших потребляющих классах.

Ваши пользователи кажутся просто данными.Если вы сделаете эти данные окружающим контекстом, который может быть доступен и отредактирован повсюду в вашем приложении, вас укусит.Вы не знаете, кто что делает с этими данными и когда он это делает.Это похоже на состояние сеанса.

Таким образом, явная зависимость от ваших данных была бы первым шагом для отслеживания злоупотребления этими данными.

Если для вас имеет смысл создать«объект держателя данных», который имеет свойство для Users или напрямую внедряет эти данные в ваших потребителей, остается за вами.Если данных больше, чем просто Users, заманчиво поместить их все в один и тот же объект центрального хранилища данных, даже если они не нужны вашим конкретным потребителям.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...