IoC-контейнер в ASP.NET с использованием шаблона MVP (Model-View-Presenter)

25 апреля 2010 г.

После видео про управление зависимостями в коде многие интересуются, как же сделать тоже самое в классическом ASP.NET. Проблема в том, что нужно найти точку старта для активного внесения зависимостей. В ASP.NET MVC она создана за нас – это фабрика контроллеров IControllerFactory.

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

Дмитрий Воробьёв прислал мне ссылку на серию отличных видео про использование IoC-контейнеров в ASP.NET . Наиболее интересная статья из этой серии – Unity IoC Dependency Injection and ASP.NET Model-View-Presenter.

Очень важно понимать, что ASP.NET WebForms – это только один из способов работы пользователя с данными вашей системы. От aspx-страницы требуется только отобразить данные или вернуть ввод пользователя. Хочу предостеречь вас от ошибки. Не пытайтесь писать бизнес-логику прямо в code-behind. Её нельзя протестировать и такой подход ведёт к многочисленному дублированию кода и ошибкам в проектировании.

Для ASP.NET и Windows-приложений лучше всего использовать шаблон Model-View-Presenter. Отличное описание этого шаблона вы найдете в книге Роберта Мартина Agile Principles, Patterns, and Practices in C# в главе The Payroll User Interface: MODEL VIEW PRESENTER. В этой главе подробно рассмотрено, как применять шаблон MVP и преимущества, которые мы получаем:

  • разделение отображения данных View и их подготовки и обработки Presenter
  • возможность модульного тестирования

Кроме этого, перед нами открывается новая возможность – использование IoC-контейнера. Точкой старта для активного внесения зависимостей будет создание Presenter'а.

Я реализовал одно из возможных решений и добавил его в исходный код проекта с ASP.NET MVC. Вся реализация находится в двух проектах: Web.Classic и Web.Classic.UI. Можете развивать его дальше и, если получится что-то интересное, присылайте мне. Рассмотрим его достоинства и выложим исходный код.

Более элегантное решение

Одной из возможных альтернатив моему решению может быть использование фабрики aspx-страниц. Подход подробно описан в статье Inversion of Control and Dependency Injection with WebForms. Это решение чем-то напоминает фабрику контроллеров в ASP.NET MVC.

15 комментариев:

  1. Хотелось бы в примере хоть какого-нибудь инпута. И не только с текстбокса по щелчку на кнопку. Какой-нибудь FormView бы рассмотреть

    ОтветитьУдалить
  2. @Dimonina
    Ок. Кроме FormView еще с чем-нибудь есть проблемы?

    ОтветитьУдалить
  3. да я в общем на входные данные хотел бы посмотреть, просто когда такой банальный вывод - это все равно просто и понятно. Да, разложили "по полочкам", можно оттестировать, все красиво и клево.
    Но взять тот же ListView с пейджером. Для вывода пейджинга (не знаю как в новом 4.0 асп.нет, может придумали что-то) нужно обязательно использовать какой-нибудь ХХХдатасурс. Взять тот же обджектдатасурс. Для вывода списка с пейджером (асп.датапейджер) должна работать связка
    ListView -> ObjectDataSource -> HelperClass -> BizLogic.
    Где хелперкласс - некая прокся на бизнес-логику, грубо говоря
    GetListOfItems(int startRowIndex, int maximumRows)
    {
    return this._itemLogic.AllItems().Skip(startRowIndex).Take(maximumRows);
    }

    Получается что вызов логики идет не из странички, которая в пейдж-лоаде функцию презентера дергает, а из отдельного хелпер-класса к обджектдатасурсу. Как туда инжектировать зависимости? Очевидно, создав базовый хелпер и в его конструкторе зарезолвить зависимости, как это сделано, допустим, для юзерконтролов в каком-то примере из статьи. И как в таком случае сюда подрутить модель MVP? Делать зависимым хелпер-класс от презентера а не от логики напрямую? Хотелось бы в общем по составным контролам какой-нибудь опыт увидеть, потому что в простом примере все понятно. Если нужно, могу тестовый проект сделать с комментариями.

    ОтветитьУдалить
  4. @Dimonina
    На самом деле все проще, чем вы думаете.

    Закомитил в исходники пример работы с ListView.

    Чтобы вам было легче дальше продвигаться в этой теме, прочитайте главу из книги Роберта Мартина http://my.safaribooksonline.com/0131857258/ch38
    в ней очень подробно рассмотрено применение шаблона MVP.

    ОтветитьУдалить
  5. Спасибо, Саш. Про мвп обяз почитаю.
    Скачал ЛистВью демо. Собственно это опять простой пример c датабайндингом. Я говорил о связке ListView и DataPager. В примере из SVN-а номер страницы задается по квери-стринг параметру, но самого пейджера на странице не нашел (запустить проект не могу, на данной машине только экспресс версия веб-девелопера 2010, чета не хочет). То есть в данном случае пейджер придется реализовать самостоятельно?

    ОтветитьУдалить
  6. @Dimonina
    Если честно, я не понимаю в чем проблема =)
    Можно и нужно использовать элементы отображения, которые тебе удобны. Например, используй DataPager, только данные для него подготовь в Presenter, а не в code-behind.

    ОтветитьУдалить
  7. Так в том то и дело что есть такой затык, что нельзя использовать датапейджер и листвью без использования компонента ХХХдатасурс на странице, который, в свою очередь (если речь про обжект датасурс) требует ссылки на класс с логикой или проксей на логику, иначе пейджинг не будет работать. Я сейчас посмотрю как с этим дело в 4 фреймворке, если сделали возможность задавать без датасурсов - вопрос снят, иначе проблема для меня остается открытой. Давай так, я покопаюсь в 4 аспнете, напишу че нарыл :-)

    ОтветитьУдалить
  8. @Dimonina
    Ок, но будет лучше, если ты пришлешь мне реализацию ListView и DataPager с использованием MVP. Пиши на мыло, как будут результаты. Если не получится, то я добавлю в исходники свою версию =)

    ОтветитьУдалить
  9. В таком случае, при использовании MVP нам нет необходимости в сборке BusinessLogic.Services, т.к. вся логика будет в presenter?

    ОтветитьУдалить
  10. @s-stude
    В презентере будет логика управления UI (в данном случае WebForms), а в слое бизнес-логики работа с сущностями, выполнение бизнес-операций, например, активация аккаунта или добавления продукта в корзину. Презентер может только попросить бизнес-логику о выполнении этих операций.

    ОтветитьУдалить
  11. Cуществует какая-то очередность при байнде зависимостей? Т.к. у меня сейчас вылетает StackOverflowException в Ninject.Core
    (Пытаюсь разрешить зависимости, использую еще MVP для WinForms)

    ОтветитьУдалить
  12. И еще один момент - не совсем ясен процесс ресолвинга presenter'a во view. У меня, почему-то, возвращается или новый инстанс view или presenter'a

    ОтветитьУдалить
  13. @s-stude
    > Cуществует какая-то очередность при байнде зависимостей?

    Нет

    > У меня, почему-то, возвращается или новый инстанс view или presenter'a

    Надо смотреть код. Скидывайте мне на почту

    ОтветитьУдалить
  14. ASP.NET - это и так MVP, совершенно не вижу смысла усложнять себе жизнь вводя еще один presenter.

    В чистом ASP.NET для разрыва зависимостей DI подходит не очень, но можно воспользоваться фабриками, которые являются все тем же IoC.

    ОтветитьУдалить
  15. @Nikolay Sergeev

    > ASP.NET - это и так MVP

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

    > воспользоваться фабриками, которые являются все тем же IoC

    Опять же с большой натяжкой. Для разрешения зависимостей в коде IoC гораздо удобнее фабрик. Фабрики просто инкапсулируют логику создания объектов - это да. Но у них нет единого места в настройки биндингов интерфейсов и реализаций, настройки жизненного цикла, уже не говоря о гибком конфигурировании, которое сейчас имеют большинство IoC-контейнеров.

    ОтветитьУдалить

Моя книга «Антихрупкость в IT»

Как достигать результатов в IT-проектах в условиях неопределённости. Подробнее...