Как правильно создать службу, которая выполняет вызовы из базы данных через репозиторий? - PullRequest
2 голосов
/ 13 января 2012

Я работаю над сервисом, который выполняет операции с базой данных через репозиторий. В сервисе я создаю экземпляр репозитория, для которого требуется контекст базы данных в конструкторе. Я хотел знать, должен ли контекст передаваться в Сервис, или код в порядке. Или было бы лучше передать объект Repository в службу для его использования? Как должен выглядеть код пользовательского интерфейса при использовании класса Service?

public class Service
    {
        private IRepository<WWW> _repository;

        public Service()
        {
            _repository = new Repository<WWW>(new DBContext());
        }

        public WWW GetWWW(int wwwID)
        {
            return _repository.Get(x => x.WWWID == wwwID).FirstOrDefault();
        }

        public void AddWWW(WWW www)
        {
            _repository.Add(www);
        }

        public void DeleteWWWByID(int wwwID)
        {
            _repository.Delete(x => x.WWWID == wwwID);
        }

        public void SaveChanges()
        {
            _repository.SaveChanges();
        }
    }

Ответы [ 2 ]

2 голосов
/ 13 января 2012

Действительно, было бы лучше передать хранилище Сервису через конструктор Сервиса, например:

public class Service
{
    private readonly IRepository<WWW> _repository;

    public Service(IRepository<WWW> repository)
    {
        _repository = repository;
    }

    /* the rest is unchanged */
}

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

public class UIClass : BaseClassDictatedByCurrentUIFramework
{
    private readonly Service _service;

    public UIClass(Service service)
    {
        _service = service;
    }

    /* UI code that will eventually call methods on the service */
}

Далее необходимо настроить контейнер Inversion of Control, который будет знать, как разрешать экземпляры IRepository (и, при необходимости, подавать им соответствующий экземпляр DataContext).

Например, если наш код пользовательского интерфейса будет контроллером MVC3, мы скажем контейнеру разрешить экземпляр этого контроллера. Вот что происходит:

  • Контейнер замечает зависимость от Service (в конструкторе) и пытается разрешить ее.

  • Поскольку Service является конкретным классом, контейнер попытается разрешить его, а затем обратите внимание на зависимость от IRepository<WWW>.

  • Разрешение IRepository, так как это интерфейс, требует, чтобы контейнер был предварительно настроен на «знание», что возвращать, когда его запрашивают экземпляр его. Обычно это всего лишь отображение между интерфейсом и его конкретной реализацией. В нашем случае конкретная реализация - Repository<WWW>, и контейнер также отвечает за «знание» того, как создать для него необходимый экземпляр DataContext (это также необходимо предварительно настроить)

  • Имея экземпляр репозитория, контейнер может правильно создать сначала экземпляр Service, а затем класс контроллера.

Обратите внимание, что автоматическое разрешение конкретных классов является функцией, которую имеют не все контейнеры IoC; некоторые для этого требуют явной настройки.

Кроме этого, я думаю, класс Service не добавляет особой ценности в вашем случае. Он содержит только делегирование методов, реализованных в хранилище. В этом случае может быть лучше, чтобы пользовательский интерфейс напрямую зависел от IRepository<WWW> и просто удалял класс Service. Однако, если этот класс Service был всего лишь примером и в вашем реальном проекте реализованы реальные бизнес-правила, то его следует сохранить.

Обновление: как разрешить зависимости в ASP.Net Webforms

Пример, который я привел выше, является идеальным сценарием внедрения зависимости. Это будет работать, например, в ASP.NET MVC, где BaseClassDictatedByCurrentUIFramework будет Controller - в этом случае инфраструктура позволяет нам контролировать компоненты, которые создают экземпляры контроллеров, поэтому мы можем внедрить наши собственные зависимости в конструкторе.

Тем не менее, ASP.Net WebForms не очень дружественна к DI-среде. Требуется, чтобы каждый Page имел конструктор по умолчанию, что делает все идеи внедрения конструктора не подходящими.

В этом случае одним из возможных решений будет следующий компромисс:

  • создать экземпляр контейнера IoC при запуске приложения (в Globals.asax) и сделать его доступным через некоторое глобальное состояние (например, контекст приложения)
  • в классе Page объявляет зависимости как private readonly поля, но конструктор не будет иметь соответствующих параметров (у него вообще не будет параметров)
  • в теле конструктора:
    • получить ссылку на контейнер IoC (доступно из глобального состояния)
    • использовать контейнер для разрешения всех зависимостей класса

Обратите внимание, что этот подход требует дисциплины - легко использовать контейнер где-то еще, чем конструктор для разрешения других компонентов. Этот подход называется Service Locator, он считается антипаттерном (http://blog.ploeh.dk/2010/02/03/ServiceLocatorIsAnAntiPattern.aspx) и поэтому его следует избегать.

0 голосов
/ 13 января 2012

Я бы предложил увеличить инверсию зависимостей. Лично я рассматриваю это не как то, должны ли вы передавать сервис DBContext или нет, а в том, что вы должны передавать сервис самому репозиторию.

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