none
Классы, которые работают с HttpContext, держать вне IoC. Что скажете? RRS feed

  • Общие обсуждения

  • Всем доброго дня!

    Недавно пришел к выводу, что классы, которые работают с HttpContext и HttpContextBase - держать вне инверсии контроля.

    1 Причины:

    1.1) Обычно инициализация DependencyResolver происходит в Application_Start() - а это значит один экземпляр контейнера доступен не только на уровне Http запроса, а доступен на уровне всего приложения и сразу всем пользователям.

    1.2) Некоторые IoC сохраняют созданные объекты на протяжении всей жизни контейнера. Даже, если этого и не нужно. К примеру, я так и не смог заставить Autofac, чтобы при каждом запросе заново создавался привязанный объект класса описанный в конфигурации, как: build.RegisterType<MyClass>().InstancePerRequest(); Ninject же - тут работает как надо. Т.е. в итоге мы упираемся в реализацию конкретного DI - что не очень корректно с точки зрения  стабильности системы.

    2 Выводы:

    2.1) HttpContext - нужно иметь постоянно актуальный - это безопасность и базовая инфраструктура приложения и доступ к объектам класса, которые с ним работают, должен быть максимально прозрачен на этапе всего приложения.

    2.2) Приложение не должно зависеть от конкретной реализации IoC

    2.3) Если вне контроля версий держать, то объект создавать напрямую через new, либо если лениво  через статический метод класса MyClass.GetObject(){return new MyClass();}

    2.4) Обычно кастомных классов, которые работают, с HttpContext в приложении не так много, и их применение не везде требуется. Поэтому большого дискомфорта быть не должно.

    P.S.: Натолкнуло написать просмотр одного проекта, где разработчик использовал всякого рода хаки для того чтобы HttpContext был актуальный в контейнере IoC.



    16 апреля 2015 г. 4:51

Все ответы

  • Что-то у вас путаница - IoC это внедрение зависимостей, а контроль версий это вообще иное. Ну да наверное опечатались.

    Если вы используете MVC, то HttpContext не должен уходить дальше контроллеров. Получили запрос, валидировали, вытянули данные, и уже их в "чистом" виде перенаправили дальше. Как только BL отработал - собираем ответ. В этом случе BL вообще без разницы WebApp у вас, Desktop или Mobile. Получается BL, DA прочее можно тестировать без заморочек с контекстом.

    Ну а как тестировать контроллеры - в сети куча примеров. те же моки :)

    16 апреля 2015 г. 6:33
  • "того чтобы HttpContext был актуальный в контейнере IoC." - тут вообще непонятно что? А можно узнать что за приложение вы пытаетесь тестировать?

    Сделаем содержимое сообщества лучше, вместе!

    16 апреля 2015 г. 6:46
    Модератор
  • >>>Ну да наверное опечатались.

    Вы правы. Опечатался, сорри. Торопился.

    >>>HttpContext не должен уходить дальше контроллеров

    В простых системах - согласен.

    Но в больших проектах, куда тогда помещать логику по работе к примеру с пользователями (а там тоже нужен порой HttpContext - нужно получение актуального пользователя и прочие операции)?

    Авторизация, регистрация - это понятно, можно в контроллере типа AccountController, но, к примеру, при создании или редактировании объектов (тех же блогов, форумы, соц сети) привязанных к пользователям - тут необходимы дополнительные проверки текущего пользователя с владельцем записи, в каких он группах (группы != роли, а именно группы) состоит, каково его отношения к другим элементами бизнес-логики. Если все поместить в  экшен контроллеров, так будут дубли кода.

    По Вашему куда выносить этот код? 

    16 апреля 2015 г. 7:07
  • CMS - одного популярного развлекательного сайта.
    16 апреля 2015 г. 7:52
  • С такими большими CMS не работал.

    Но по своему скромному опыту скажу, что возникали такие точки соприкосновения БизнесЛогики и HttpContext-a и да - повторяющийся набор операций лучше обособлять в каком либо классе.

    По теме: инверсию и HttpContext уж точно лучше не мешать.

    Ну а по поводу хаков - я недавно встретился с хранением контекста базы данных в контейнере IoC - и с недоуменным возгласом владельцев проекта, что ложится сервер MsSql.

    16 апреля 2015 г. 8:32
  • Авторизация, регистрация - это понятно, можно в контроллере типа AccountController, но, к примеру, при создании или редактировании объектов (тех же блогов, форумы, соц сети) привязанных к пользователям - тут необходимы дополнительные проверки текущего пользователя с владельцем записи, в каких он группах (группы != роли, а именно группы) состоит, каково его отношения к другим элементами бизнес-логики. Если все поместить в  экшен контроллеров, так будут дубли кода.

    По Вашему куда выносить этот код? 

    А почему тут не подойдет следующая схема

    • запрос попадает в контроллер
    • вытаскиваем из запроса все нужные данные (например при редактировании, новая версия текста заметки в блог и код заметки которую правили)
    • получаем Id авторизованного пользователя
    • дергаем из BL класс, отвечающий за проверку прав (передаем UserId, PostId. Все что еще надо BL сама возьмет из БД)
    • если прав хватает вызываем класс, ответственный за правку статьи (потребуется PostId и новый текст). Возвращаем 200.
    • если не хватило прав, то юзер ловит 401 или 403 в зависимости от причины.

    Более того, я бы еще сделал так, что непосредственно из контроллера (т.е. читай из вне самой BL) не был бы доступен интерфейс, отвечающий за правку поста (и прочие критичные операции). Вместо этого создаются специальные интерфейсы, которые по сути являются командами. Контроллер дергает их, а уже они занимаются проверкой прав и выполнением действий. Например - команда зарегать юезра, проверит данные, вызовет DAL для проверки не занят ли уже email, если не занят - зарегает и вышлет письмо с кодом подтверждения.

    В итоге контроллер получается очень простым, а вся логики в BL которые не зависит от контекста.

    Я понимаю что проще кинуть весь контекст по конвейеру, а не выбирать нужные данные. Но это как раз проще (и то до первых тестов), а не лучше.

    16 апреля 2015 г. 9:16
  • >>>Вместо этого создаются специальные интерфейсы, которые по сути являются командами

    А можно подробней описать как вы их видите эти "интерфейсы"?

    1) Классы с набором статичных методов?

    2) В IoC-контейнере, как я понял, их не регистрировать.

    3) Получается набор статичных хэлперов. верно? 

    16 апреля 2015 г. 10:55
  • 3) Получается набор статичных хэлперов. верно? 

    Нет. Попробую кратко описать, т.к. с примерами на заметку в блоге выйдет :)

    1) Внешние сервисы (по отношению к сайту это рассылка мыла, смс, чтение rss итд) это - сервисы. Пары интерфейс + его реализация лежат в контейнере.

    2) CQRS для работы с DAL. Каждый запрос это интерфейс, есть фабрика, которая их ресолвит. Объекты BL работают с интерфейсами и классами команд через фабрики. Классы неболшие и легко тестируемые.

    3) Команды для контроллера, содержат логику и используют запросы к БД через (2). Так же пары интерфейс + реализация.

    4) В контроллер инжектим в команды через конструктор (интерфесы, которые ресолвит Dep.Resolver). Для простых операций контроллер может использовать фабрики напрямую.

    Собственно начиная от контроллера все по очереди ресолвится и инжектится. В тестах все что надо мокается, начиная от EF (который используется для DAL). По необходимости команды и сервисы могут быть снглтонами через настройку контейнера. Остальное все pre request.

    16 апреля 2015 г. 13:07
  • Андрей, спасибо большое за такой подробный ответ.

    Появился материал для осмысления.

    18 апреля 2015 г. 8:06