ASP.NET MVC Урок 1-F / ASP.NET MVC Урок 2
.pdf$63 1(7 09& ǻȘȖȒ 'HSHQGHQF\ ,QMHFWLRQtutorial
ASP*, .NET*
Цель урока: Изучение DI (Dependency Injection). Пример на Ninject, Unity, Autofac и Winsor.
Во многих случаях, один и тот же экземпляр класса используется в вашем приложении в разных модулях. Простым способом реализации является применение шаблона Одиночка (Singleton).
Но рассмотрим эту ситуацию с другой стороны. Так как данный объект создается при первом обращении к нему, мы не можем контролировать его время жизни. При модульном тестировании (unittest) нет необходимости использовать этот объект (или это может быть невозможно). Чтобы избежать этого, мы не напрямую вызываем объект, а через интерфейс. И реальный экземпляр класса, и экземплярзаглушка для тестирования будут реализовывать этот интерфейс. А логику создания мы поручаем DIконтейнеру.
Например, до использования сервиса. Опишем пару классов, интерфейс IWeapon с методом Kill, два класса реализации Bazuka и Sword, и класс Warrior, который пользуется оружием:
SXEOLF LQWHUIDFH ,:HDSRQ
^
YRLG .LOO
`
SXEOLF FODVV %D]XND ,:HDSRQ
^
SXEOLF YRLG .LOO
^
&RQVROH :ULWH/LQH%,* %$'$%80
`
`
SXEOLF FODVV 6ZRUG ,:HDSRQ
^
SXEOLF YRLG .LOO
^
&RQVROH :ULWH/LQH&KXN FKXFN
`
`
SXEOLF FODVV :DUULRU
^
UHDGRQO\,:HDSRQ :HDSRQ
SXEOLF :DUULRU,:HDSRQ ZHDSRQ
^
WKLV:HDSRQ ZHDSRQ
`
SXEOLF YRLG .LOO
^
:HDSRQ .LOO
`
`
Используем это:
FODVV 3URJUDP
^
VWDWLF YRLG 0DLQ VWULQJ>@ DUJV
^
:DUULRU ZDUULRU QHZ:DUULRUQHZ%D]XND
ZDUULRU .LOO
&RQVROH 5HDG/LQH
`
`
Читаем между строк. Создаем воина и даем ему базуку, он идет и убивает. В консоли получаем:
%,* %$'$%80
Заметим, что у нас нет проверки на null в строке
:HDSRQ .LOO
Что здесь некоректно? Воин не знает, есть ли у него оружие, и выдачей оружия занимается не отдельный модуль, а главная программа.
Суть DI – поручить выдачу оружия другому модулю.
Подключаем Ninject:
,QVWDOO 3DFNDJH 1LQMHFW
Создаем модуль, который занимается выдачей оружия:
SXEOLF FODVV :HDSRQ1LQMHFW0RGXOH 1LQMHFW0RGXOH
^
SXEOLF RYHUULGH YRLG /RDG
^
WKLV%LQG ,:HDSRQ! 7R 6ZRUG!
`
`
Что буквально значит: «если попросят оружие – то выдайте мечи». Создаем «сервислокатор» и пользуемся оружием:
FODVV 3URJUDP
^
SXEOLF VWDWLF,.HUQHO$SS.HUQHO
VWDWLF YRLG 0DLQ VWULQJ>@ DUJV
^
$SS.HUQHO QHZ6WDQGDUG.HUQHOQHZ:HDSRQ1LQMHFW0RGXOH
YDUZDUULRU $SS.HUQHO *HW :DUULRU!
ZDUULRU .LOO
&RQVROH 5HDG/LQH
`
`
Как видно, объект warrior мы создаем не с помощью конструкции new, а через$SS.HUQHO *HW !. При создании AppKernel, мы передаем в качестве конструктора модуль, отвечающий за выдачу оружия (в данном случае это меч). Любой объект, который мы пытаемся получить через $SS.HUQHO *HW, будет (по мере возможности) проинициализирован, если существуют модули, которые знают, как это делать.
Другой момент применения, когда объект :DUULRU не берет с собой оружие каждый раз, а при не обнаружении оного обращается к сервису локатору и получает его:
SXEOLF FODVV 2WKHU:DUULRU
^
SULYDWH,:HDSRQ BZHDSRQ
SXEOLF,:HDSRQ :HDSRQ
^
JHW
^
LIBZHDSRQ QXOO
^
BZHDSRQ 3URJUDP$SS.HUQHO *HW ,:HDSRQ!
`
UHWXUQBZHDSRQ
`
`
SXEOLF YRLG .LOO
^
:HDSRQ .LOO
`
`
Исполняем:
YDURWKHU:DUULRU QHZ2WKHU:DUULRU
RWKHU:DUULRU .LOO
Наш воин получает оружие по прямым поставкам – супер!
В Ninject есть еще одна очень хорошая деталь. Если свойство (SXEOLF SURSHUW\)
помечено >,QMHFW@, то при создании класса через $SS.HUQHO *HW ! – поле инициализуется сервисомлокатором:
SXEOLF FODVV $QRWKHU:DUULRU
^
>,QMHFW@
SXEOLF,:HDSRQ :HDSRQ ^JHW VHW`
SXEOLF YRLG .LOO
^
:HDSRQ .LOO
`
`
YDUDQRWKHU:DUULRU $SS.HUQHO *HW$QRWKHU:DUULRU!
DQRWKHU:DUULRU .LOO
Unity
Абсолютно всё то же: Установка
,QVWDOO 3DFNDJH 8QLW\
Инициализация сервиса локатора (Container)
&RQWDLQHU QHZ8QLW\&RQWDLQHU
Регистрация типа
&RQWDLQHU 5HJLVWHU7\SHW\SHRI,:HDSRQW\SHRI%D]XND
Получение объекта и использование:
YDUZDUULRU &RQWDLQHU 5HVROYH :DUULRU!
ZDUULRU .LOO
Кроме того, у Unity есть классодиночка 6LQJOHWRQ 6HUYLFH/RFDWRU, который регистрирует контейнер и позволяет получить доступ к сервисам из любого места.
YDUVHUYLFH3URYLGHU QHZ8QLW\6HUYLFH/RFDWRU &RQWDLQHU
6HUYLFH/RFDWRU 6HW/RFDWRU3URYLGHU ! VHUYLFH3URYLGHU
Хитрый 2WKHU:DUULRU теперь так получает оружие:
SXEOLF FODVV 2WKHU:DUULRU
^
SULYDWH,:HDSRQ BZHDSRQ
SXEOLF,:HDSRQ :HDSRQ
^
JHW
^
LIBZHDSRQ QXOO
^
BZHDSRQ 6HUYLFH/RFDWRU &XUUHQW *HW,QVWDQFH ,:HDSRQ!
`
UHWXUQBZHDSRQ
`
`
SXEOLF YRLG .LOO
^
:HDSRQ .LOO
`
`
Autofac
Так же, собственно, всё и происходит: Установка
,QVWDOO 3DFNDJH$XWRIDF
Инициализация строителя сервисалокатора (&RQWDLQHU%XLOGHU) – нетнет, это еще не сам контейнер, это — как модули
YDUEXLOGHU QHZ&RQWDLQHU%XLOGHU
Регистрация типов. Надо зарегистрировать все необходимые классы, потому что создание экземпляров незарегистрированных классов тут не реализован.
EXLOGHU 5HJLVWHU7\SH %D]XND!
EXLOGHU 5HJLVWHU7\SH :DUULRU!
EXLOGHU 5HJLVWHU ,:HDSRQ! [ ! [ 5HVROYH %D]XND!
Создание сервиса локатора (Container)
YDUFRQWDLQHU EXLOGHU %XLOG
Получение объекта и использование:
YDUZDUULRU FRQWDLQHU 5HVROYH :DUULRU!
ZDUULRU .LOO
Castle Windsor
Установка
,QVWDOO 3DFNDJH &DVWOH :LQGVRU
Инициализация сервисалокатора
YDUFRQWDLQHU QHZ:LQGVRU&RQWDLQHU
Регистрация типов. Аналогична как и в Autofac.
FRQWDLQHU 5HJLVWHU &RPSRQHQW )RU ,:HDSRQ! ,PSOHPHQWHG%\ %D]XND!
&RPSRQHQW )RU :DUULRU! ,PSOHPHQWHG%\ :DUULRU!
Получение объекта и использование:
YDUZDUULRU FRQWDLQHU 5HVROYH :DUULRU!
ZDUULRU .LOO
Маленький подитог
На самом деле, реализации Dependency Injection не сильно, но всё же отличаются. Некоторые поддерживают инициализацию в :HE FRQILJ$SS FRQILJ файлах. Некоторые, задают правила для инициализации, как мы сейчас посмотрим на расширении Ninject для asp.net mvc – это касается инициализации сервисалокатора как генератора общих объектов, так и отдельно для каждого потока или webзапросе.
Объекты областей (Ninject)
В Ninject можно задать несколько способов инициализации получения объекта из класса. Если мы работаем в различных контекстах (например, в разных потоках (Thread)), то объекты должны быть использованы разные. Тем самым, поддерживается масштабируемость и гибкость приложения.
Облас |
Метод |
Объяснение |
ть |
связывания |
|
|
|
|
Време |
,Q7UDQVLHQW |
Объект класса будет создаваться по каждому |
нный |
6FRSH |
требованию (метод по умолчанию). |
Одино |
,Q6LQJOHWRQ |
Объект класса будет создан один раз и будет |
чка |
6FRSH |
использоваться повторно. |
Поток |
,Q7KUHDG6FR |
Один объект на поток. |
|
SH |
|
Запрос |
,Q5HTXHVW6F |
Один объект будет на каждый webзапрос |
|
RSH |
|
Lifetime Manager в Unity
В Unity для задачи правил инициализации используется реализация абстрактного класса
LifetimeManager.
Происходит это так:
BFRQWDLQHU 5HJLVWHU7\SH 'E&RQWH[W 6DYHFDVK7UDYHO&RQWH[W!QHZ3HU5HTXHVW/LIHWLPH0DQDJHU
Где PerRequestLifetimeManager – это реализация LifetimeManager:
SXEOLF FODVV 3HU5HTXHVW/LIHWLPH0DQDJHU /LIHWLPH0DQDJHU
^
VXPPDU\!
.H\ WR VWRUH GDWD
VXPPDU\!
SULYDWH UHDGRQO\ VWULQJBNH\ 6WULQJ )RUPDW6LQJOHWRQ3HU5HTXHVW^ `*XLG 1HZ*XLG
VXPPDU\!
5HWULHYH D YDOXH IURP WKH EDFNLQJ VWRUH DVVRFLDWHG ZLWK WKLV /LIHWLPH SROLF\
VXPPDU\!
UHWXUQV!
WKH REMHFW GHVLUHG RU QXOO LI QR VXFK REMHFW LV FXUUHQWO\ VWRUHG
UHWXUQV!
SXEOLF RYHUULGH REMHFW *HW9DOXH
^
LI+WWS&RQWH[W &XUUHQW QXOO +WWS&RQWH[W &XUUHQW ,WHPV &RQWDLQV BNH\
UHWXUQ+WWS&RQWH[W &XUUHQW ,WHPV>BNH\@
UHWXUQ QXOO
`
VXPPDU\!
6WRUHV WKH JLYHQ YDOXH LQWR EDFNLQJ VWRUH IRU UHWULHYDO ODWHU
VXPPDU\!
SDUDP QDPH QHZ9DOXH !7KH REMHFW EHLQJ VWRUHGSDUDP!
SXEOLF RYHUULGH YRLG 6HW9DOXH REMHFWQHZ9DOXH
^
LI+WWS&RQWH[W &XUUHQW QXOO
+WWS&RQWH[W &XUUHQW ,WHPV>BNH\@ QHZ9DOXH
`
VXPPDU\!
5HPRYH WKH JLYHQ REMHFW IURP EDFNLQJ VWRUH
VXPPDU\!
SXEOLF RYHUULGH YRLG 5HPRYH9DOXH
^
LI+WWS&RQWH[W &XUUHQW QXOO +WWS&RQWH[W &XUUHQW ,WHPV &RQWDLQV BNH\
+WWS&RQWH[W &XUUHQW ,WHPV 5HPRYH BNH\
`
`
Суть. Все объекты хранятся в +WWS&RQWH[W &XUUHQW ,WHPV>BNH\@ и выдаются только, если уже находятся в том же контексте (+WWS&RQWH[W &XUUHQW). В ином случае, создается новый объект. Если текущий контекст (+WWS&RQWH[W &XUUHQW) в области кода не существует (используем такой/LIHWLPH0DQDJHUв консольном приложении или в отдельном потоке) – то данный контейнер не будет работать.
Использование Ninject в asp.net mvc
Устанавливаем Ninject в среду asp.net mvc. Отдельно создаем свой проект LessonProject,
создадим там HomeController с методом и view Index. (/Contollers/HomeController.cs):
SXEOLF FODVV +RPH&RQWUROOHU &RQWUROOHU
^
SXEOLF $FWLRQ5HVXOW,QGH[
^
UHWXUQ9LHZ
`
`
И (/Views/Home/Index.cshtml):
#^
9LHZ%DJ 7LWOH /HVVRQ3URMHFW
/D\RXW a 9LHZV 6KDUHG B/D\RXW FVKWPO
`
K!/HVVRQ3URMHFWK!
Запускаем – работает.
Примечание: В дальнейшем мы будем переносить этот проект в последующие уроки.
Теперь установим модуль Ninject и Ninject.MVC3 для этого проекта.
,QVWDOO 3DFNDJH 1LQMHFW 09&
Добавляем класс в папку App_Start (/App_Start/NinjectWebCommon.cs):
>DVVHPEO\ :HE$FWLYDWRU 3UH$SSOLFDWLRQ6WDUW0HWKRGW\SHRI/HVVRQ3URMHFW$SSB6WDUW 1LQMHFW:HE&RPPR Q6WDUW@ >DVVHPEO\ :HE$FWLYDWRU$SSOLFDWLRQ6KXWGRZQ0HWKRG$WWULEXWHW\SHRI/HVVRQ3URMHFW$SSB6WDUW 1LQMHFW :HE&RPPRQ6WRS@
QDPHVSDFH /HVVRQ3URMHFW$SSB6WDUW
^
XVLQJ6\VWHP
XVLQJ6\VWHP :HE
XVLQJ0LFURVRIW :HE ,QIUDVWUXFWXUH '\QDPLF0RGXOH+HOSHU
XVLQJ1LQMHFW
XVLQJ1LQMHFW :HE &RPPRQ
SXEOLF VWDWLF FODVV 1LQMHFW:HE&RPPRQ
^
SULYDWH VWDWLF UHDGRQO\%RRWVWUDSSHU ERRWVWUDSSHU QHZ%RRWVWUDSSHU
VXPPDU\!
6WDUWV WKH DSSOLFDWLRQ
VXPPDU\!
SXEOLF VWDWLF YRLG 6WDUW
^
'\QDPLF0RGXOH8WLOLW\ 5HJLVWHU0RGXOHW\SHRI2QH3HU5HTXHVW+WWS0RGXOH
'\QDPLF0RGXOH8WLOLW\ 5HJLVWHU0RGXOHW\SHRI1LQMHFW+WWS0RGXOH
ERRWVWUDSSHU ,QLWLDOL]H &UHDWH.HUQHO
`
VXPPDU\!
6WRSV WKH DSSOLFDWLRQ
VXPPDU\!
SXEOLF VWDWLF YRLG 6WRS
^
ERRWVWUDSSHU 6KXW'RZQ
`
VXPPDU\!
&UHDWHV WKH NHUQHO WKDW ZLOO PDQDJH \RXU DSSOLFDWLRQ
VXPPDU\!
UHWXUQV!7KH FUHDWHG NHUQHOUHWXUQV!
SULYDWH VWDWLF,.HUQHO&UHDWH.HUQHO
^
YDUNHUQHO QHZ6WDQGDUG.HUQHO
NHUQHO %LQG )XQF ,.HUQHO!! 7R0HWKRG FW[ ! !QHZ%RRWVWUDSSHU .HUQHO
NHUQHO %LQG ,+WWS0RGXOH! 7R +WWS$SSOLFDWLRQ,QLWLDOL]DWLRQ+WWS0RGXOH!
5HJLVWHU6HUYLFHV NHUQHO
UHWXUQNHUQHO
`
VXPPDU\!
/RDG \RXU PRGXOHV RU UHJLVWHU \RXU VHUYLFHV KHUH
VXPPDU\!
SDUDP QDPH NHUQHO !7KH NHUQHOSDUDP!
SULYDWH VWDWLF YRLG 5HJLVWHU6HUYLFHV,.HUQHO NHUQHO
^
`
`
`
В RegisterServices мы добавляем инициализацию своих сервисов. Для начала добавим шутливый IWeapon, а в дальнейшем еще будем возвращаться к этому методу для регистрации других сервисов:
SXEOLF LQWHUIDFH ,:HDSRQ
^
VWULQJ .LOO
`
«
SXEOLF FODVV %D]XND ,:HDSRQ
^
SXEOLF VWULQJ .LOO
^
UHWXUQ %,* %$'$%80
`