ASP.NET MVC Урок 1-F / ASP.NET MVC Урок D
.pdf$63 1(7 09& ǻȘȖȒ ' 6FDIIROGLQJ tutorial
ASP*, .NET*
Цель урока. Научиться использовать Scaffolding для создания прототипа проекта. Определяем и фиксируем структуру репозитория. Простая и языковая версия класса. Тестируем использование Scaffolderа, используем «направляющие» атрибуты. Параметры для Scaffolderа. Создание управляющих атрибутов. Полный цикл создания и управления объекта в админке.
Scaffolding T4 для Visual Studio 2013 не применимо.
Scaffolding. Начало.
В этом и следующем уроке мы изучим то, что поможет вам в разы быстрее разрабатывать приложения. Начнем издалека. Когда я делал первый сайт, я смотрел, как можно реализовать тот или иной функционал и использовал его у себя в приложении. Потом, когда у меня появился второй проект, я начал функционал улучшать. Я выделил основные моменты и инструменты, которые были описаны в предыдущих уроках. Я начал замечать, что я делаю часто много механичной работы, например:
создать в БД новую таблицу
прокинуть ее в класс DbContext
добавить объявление в интерфейс репозитария
добавить реализацию в SqlRepository
добавить partialчасть класса в папке Proxy
добавить модель данных
объявить mapping
создать контроллер в админке
сделать типичные view для просмотра и редактирования
Итак как это было поистину скучно, я часто ошибался в одном из шагов – и нужно было править банальные ошибки. И я создал сниппеты, но они решали только половину задачи, а вот модель данных, контроллер, index.cshtml, edit.cshtml – это не было решено.
Ивот я прочитал статью Стивена Сандерсона «Scaffold your ASP.NET MVC 3 project with the MvcScaffolding package» и загорелся. Скаффолдинг подходил мне идеально, но он не был написан для моего решения. И я начал изучать. В основе его стоял T4 (Text Template Transformation Toolkit), в шаблонах используется именно этот синтаксис, но для работы дошаблонной логики используется Windows PowerShell. Собственно, с PowerShell мы работаем в
PackageManager Console (ух, как закручено!). Я совсем немного погружусь в Windows PowerShell
и T4, только для того, чтобы создать пару скаффолдеров для работы с проектом.
Итак, что нам изначально необходимо, так это установить PowerGUI для работы с PowerShell. В VS2010 есть много редакторов для PowerShell. Но мы работаем с VS2012 и нам пока так не повезло.
Ок, установили. Переходим к установке редактора для t4 http://t4editor.tangible engineering.com. Тоже пока что единственный редактор для VS2012. Ну что ж – подсветочка есть и ладно.
T4
Далее изучим, что у нас есть. Начнем с T4. Я пользовался этой ссылкой:http://www.olegsych.com/2007/12/texttemplatetransformationtoolkit/
Создадим новый проект, библиотеку классов LesssonProject.T4. И добавим туда HelloWorld.tt:
Изменим немного:
# WHPSODWH GHEXJ WUXH KRVW6SHFLILF WUXH ! # RXWSXW H[WHQVLRQ FV !
#$VVHPEO\ 1DPH 6\VWHP &RUH ! #$VVHPEO\ 1DPH 6\VWHP :LQGRZV )RUPV ! # LPSRUW QDPHVSDFH 6\VWHP ! # LPSRUW QDPHVSDFH 6\VWHP ,2 ! # LPSRUW QDPHVSDFH 6\VWHP 'LDJQRVWLFV ! # LPSRUW QDPHVSDFH 6\VWHP /LQT ! # LPSRUW QDPHVSDFH 6\VWHP &ROOHFWLRQV !
# LPSRUW QDPHVSDFH 6\VWHP &ROOHFWLRQV *HQHULF !
YDUJUHHWLQJ +HOOR :RUOG !
7KLV LV WKH RXWSXW FRGH IURP \RXU WHPSODWH
\RX RQO\ JHW V\QWD[ KLJKOLJKWLQJ KHUH QRW LQWHOOLVHQVH QDPHVSDFH 0\1DPH6SDFH
^
FODVV 0\*HQHUDWHG&ODVV
^
|
|
VWDWLF YRLG PDLQ VWULQJ>@ DUJV |
|
|
|
^ |
|
|
|
|
6\VWHP &RQVROH :ULWH/LQH JUHHWLQJ ! |
|
|
` |
|
|
` |
|
|
` |
|
|
|
|
|
|
|
|
|
|
|
,QVHUW DQ\ WHPSODWH SURFHGXUHV KHUHYRLG IRR^`
!
Ок, и результатом этого будет:
7KLV LV WKH RXWSXW FRGH IURP \RXU WHPSODWH
\RX RQO\ JHW V\QWD[ KLJKOLJKWLQJ KHUH QRW LQWHOOLVHQVH QDPHVSDFH 0\1DPH6SDFH
^
FODVV 0\*HQHUDWHG&ODVV
^
|
|
VWDWLF YRLG PDLQ VWULQJ>@ DUJV |
|
|
|
^ |
|
|
|
|
6\VWHP &RQVROH :ULWH/LQH+HOOR :RUOG |
|
|
` |
|
|
` |
|
|
` |
|
|
|
На самом деле .tt файл преобразуется в код, который создает конкретный класс, наследуемый от TextTransformation. Этот код запускается и генерируется файлрезультат. Выглядит примерно так:
# WHPSODWH ODQJXDJH & !
+HOOR :RUOG
Преобразуется в:
SXEOLF FODVV *HQHUDWHG7H[W7UDQVIRUP 0LFURVRIW 9LVXDO6WXGLR 7H[W7HPSODWLQJ 7H[W7UDQVIRUPDWLRQ
^
SXEOLF RYHUULGH VWULQJ 7UDQVIRUP7H[W
^
WKLV:ULWH+HOOR :RUOG
UHWXUQ WKLV*HQHUDWLRQ(QYLURQPHQW 7R6WULQJ
`
`
А итогом будет файл .cs:
+HOOR:RUOG
Изучим блоки и синтаксис задания шаблонов, который очень похож на aspx, только вместо скобок <% %> используется <# #>. Но, так как aspx мы не изучали, то:
Текстовый блок – это любой не программный текст в тексте шаблона (извините за тафтологию):
# WHPSODWHODQJXDJH &!
+HOOR :RUOG
Блок операторов – это любой блок, заключенный в <# #>. Всё что внутри этого, это языковая конструкция, которая задает логику построения текста:
YDUJUHHWLQJ +HOOR :RUOG
!
Блок выражения – это блок, заключенный в <#= #>. Всё, что внутри этого блока, будет приведено к строке и добавлено в текст шаблона:
6\VWHP &RQVROH:ULWH/LQH JUHHWLQJ !
Блок функций – это блок, заключенный в!. Все функции, объявленные в этом блоке, могут быть вызваны в шаблоне. Кроме того, сами функции могут содержать текст шаблона. Директива# WHPSODWH ! – позволяет задать характеристики класса преобразования из шаблона:
# WHPSODWH ODQJXDJH ´& ´! – задает язык класса.
# WHPSODWH GHEXJ ´WUXH´!– позволяет отладить генерацию шаблона.
# WHPSODWH LQKHULWV ´0\7H[W7UDQVIRUPDWLRQ´! – указывает, какой класс должен
быть использован в качестве базового для класса генерации в процедуре генерации файла.
Директива# RXWSXW ! – задает расширение для генерируемого файла:
# RXWSXWH[WHQVLRQ FV !
Директива # LPSRUW ! – добавляет использование в процедуре исполнения заданных namespace. То же самое что и using добавить (но не в результат, а при выполнении генерации текста):
#LPSRUWQDPHVSDFH 6\VWHP &ROOHFWLRQV!
Директива # DVVHPEO\ ! – добавляет объявление сборки. То же самое, что и в
VisualStudio добавить Reference:
#$VVHPEO\ 1DPH 6\VWHP &RUH!
Директива # LQFOXGH ! – добавляет некий другой шаблон в месте объявления. Это как Html.Partial():
#LQFOXGHILOH ,QFOXGHG WW !
Директива# SDUDPHWHU ! — добавляет параметр при формировании шаблона. Но передача его происходит настолько сложно, что пример я приводить не буду. Ссылка По остальному – смотрите по ссылке.
В общем, этих знаний и знаний по Reflection вполне хватит, чтобы сгенерировать нужные нам файлы, но перейдем к проекту MvcScaffolding.
MVCScaffolding
Установим T4Scaffolding:
30! ,QVWDOO3DFNDJH7 6FDIIROGLQJ
Создадим папку CodeTemplates/Scaffolders/IRepository в LessonProject.Model в ней добавим файлы IRepository.ps1 (LessonProject.Model/CodeTemplates/Scaffolders/IRepository/IRepository.ps1):
>7 6FDIIROGLQJ 6FDIIROGHU 'HVFULSWLRQ &UHDWH ,5HSRVLWRU\ LQWHUIDFH@>&PGOHW%LQGLQJ @
SDUDP
>SDUDPHWHU 0DQGDWRU\ WUXH 9DOXH)URP3LSHOLQH%\3URSHUW\1DPH WUXH @>VWULQJ@ 0RGHO7\S
H
>VWULQJ@ 3URMHFW
>VWULQJ@ &RGH/DQJXDJH
>VWULQJ>@@ 7HPSODWH)ROGHUV
>VZLWFK@ )RUFH IDOVH
IRXQG0RGHO7\SH *HW 3URMHFW7\SH 0RGHO7\SH 3URMHFW 3URMHFW %ORFN8L LIIRXQG0RGHO7\SH ^UHWXUQ`
)LQG WKH ,5HSRVLWRU\ LQWHUIDFH RU FUHDWH LW YLD D WHPSODWH LI QRW DOUHDG\ SUHVHQW IRXQG,5HSRVLWRU\7\SH *HW 3URMHFW7\SH ,5HSRVLWRU\ 3URMHFW 3URMHFW$OORZ0XOWLSOH LIIRXQG,5HSRVLWRU\7\SH
^
&UHDWH ,5HSRVLWRU\
RXWSXW3DWK ,5HSRVLWRU\
GHIDXOW1DPHVSDFH *HW 3URMHFW 3URMHFW 3URSHUWLHV ,WHP'HIDXOW1DPHVSDFH9DOXH
$GG 3URMHFW,WHP9LD7HPSODWH RXWSXW3DWK 7HPSODWH ,5HSRVLWRU\7HPSODWH C
0RGHO #^ 1DPHVSDFH GHIDXOW1DPHVSDFH ` C
6XFFHVV0HVVDJH $GGHG ,5HSRVLWRU\ DW ^ `C
7HPSODWH)ROGHUV 7HPSODWH)ROGHUV 3URMHFW 3URMHFW &RGH/DQJXDJH &RGH/DQJXDJH )RUFH )
RUFH
IRXQG,5HSRVLWRU\7\SH *HW 3URMHFW7\SH ,5HSRVLWRU\ 3URMHFW 3URMHFW
`
$GG D QHZ SURSHUW\ RQ WKH 'E&RQWH[W FODVV LIIRXQG,5HSRVLWRU\7\SH ^
SURSHUW\1DPH IRXQG0RGHO7\SH 1DPH
SURSHUW\1DPHV *HW 3OXUDOL]HG:RUG SURSHUW\1DPH
7KLV LV D 'E&RQWH[W VR ZH FDQ IUHHO\ DGG D QHZ SURSHUW\ LI WKHUH LVQ W DOUHDG\ RQH I
RU WKLV PRGHO
$GG &ODVV0HPEHU9LD7HPSODWH 1DPH SURSHUW\1DPH &RGH&ODVV IRXQG,5HSRVLWRU\7\SH 7HPSODWH ,5HSRVLWRU\,WHP7HPSODWH 0RGHO #^
(QWLW\7\SH IRXQG0RGHO7\SH
(QWLW\7\SH1DPH3OXUDOL]HG SURSHUW\1DPHV
` 6XFFHVV0HVVDJH $GGHG SURSHUW\1DPH WR LQWHUIDFH IRXQG,5HSRVLWRU\7\SH )XOO1DP H7HPSODWH)ROGHUV 7HPSODWH)ROGHUV 3URMHFW 3URMHFW &RGH/DQJXDJH &RGH/DQJXDJH
`
UHWXUQ#^
'E&RQWH[W7\SH IRXQG'E&RQWH[W7\SH
`
Потом IRepositoryItemTemplate.cs.t4:
# 7HPSODWH /DQJXDJH & +RVW6SHFLILF 7UXH ,QKHULWV '\QDPLF7UDQVIRUP ! UHJLRQ (QY'7( &RGH7\SH 0RGHO (QWLW\7\SH 1DPH !
,4XHU\DEOH (QY'7( &RGH7\SH 0RGHO (QWLW\7\SH 1DPH !! 0RGHO (QWLW\7\SH1DPH3OXUDOL]HG !
^ JHW `
ERRO&UHDWH (QY'7( &RGH7\SH 0RGHO (QWLW\7\SH 1DPH ! (QY'7( &RGH7\SH 0RGHO (QWLW\7\S H 1DPH ! LQVWDQFH
ERRO8SGDWH (QY'7( &RGH7\SH 0RGHO (QWLW\7\SH 1DPH ! (QY'7( &RGH7\SH 0RGHO (QWLW\7\S H 1DPH ! LQVWDQFH
ERRO5HPRYH (QY'7( &RGH7\SH 0RGHO (QWLW\7\SH 1DPH ! LQW LG (QY'7( &RGH7\SH 0RGHO (QWLW \7\SH 1DPH !
HQGUHJLRQ
ɂ ,5HSRVLWRU\7HPSODWH FV W
# 7HPSODWH /DQJXDJH & +RVW6SHFLILF 7UXH ,QKHULWV '\QDPLF7UDQVIRUP !# 2XWSXW ([WHQVLRQ FV !
XVLQJ6\VWHP
XVLQJ6\VWHP &ROOHFWLRQV *HQHULF
XVLQJ6\VWHP /LQT
XVLQJ6\VWHP :HE
QDPHVSDFH 0RGHO 1DPHVSDFH !
^
SXEOLFLQWHUIDFH ,5HSRVLWRU\
^
,4XHU\DEOH 7! *HW7DEOH 7! ZKHUH 7FODVV
`
`
Создадим новую таблицу Notify:
Name |
DataType |
|
|
UserIDint (foreignKey to User)
Messagenvarchar(140)
AddedDatedatetime
IsReadedbit
Перенесем в DbContext (LessonProjectDb.dbml) и сохраняем (ctrlS):
В Package Manager Console пишем для проекта LessonProject.Model:
30! 6FDIIROG ,5HSRVLWRU\ 1RWLI\
$GGHG1RWLI\WR LQWHUIDFH/HVVRQ3URMHFW 0RGHO ,5HSRVLWRU\
Ура! Всё работает! Просто, не правда ли? Ничего не ясно? Ок, ладно разберем IRepository.ps1 по порядку:
>7 6FDIIROGLQJ 6FDIIROGHU 'HVFULSWLRQ &UHDWH ,5HSRVLWRU\ LQWHUIDFH@>&PGOHW%LQGLQJ @
SDUDP
>SDUDPHWHU 0DQGDWRU\ WUXH 9DOXH)URP3LSHOLQH%\3URSHUW\1DPH WUXH @>VWULQJ@ 0RGHO7\S
H
>VWULQJ@ 3URMHFW
>VWULQJ@ &RGH/DQJXDJH
>VWULQJ>@@ 7HPSODWH)ROGHUV
>VZLWFK@ )RUFH IDOVH
Это структура объявления кода скаффолдера. Особое внимание нужно обратить на 0RGHO7\SH –
это имя класса, именно его мы и передаем в команде 6FDIIROG ,5HSRVLWRU\ 1RWLI\. Остальные параметры идут или по умолчанию, как Force, или по умолчанию известны, как Project, CodeLanguage.
Далее мы ищем этот класс (если мы не сохранимся, то искомый класс еще не будет записан и не будет найден):
IRXQG0RGHO7\SH *HW 3URMHFW7\SH 0RGHO7\SH 3URMHFW 3URMHFW %ORFN8L
LIIRXQG0RGHO7\SH ^UHWXUQ`
Класс найден и мы переходим к следующей части. Найти файл IRepository.cs и если его нет, то создать:
)LQG WKH ,5HSRVLWRU\ LQWHUIDFH RU FUHDWH LW YLD D WHPSODWH LI QRW DOUHDG\ SUHVHQW
IRXQG,5HSRVLWRU\7\SH *HW 3URMHFW7\SH ,5HSRVLWRU\ 3URMHFW 3URMHFW$OORZ0XOWLSOH
LIIRXQG,5HSRVLWRU\7\SH
^
&UHDWH ,5HSRVLWRU\
RXWSXW3DWK ,5HSRVLWRU\
GHIDXOW1DPHVSDFH *HW 3URMHFW 3URMHFW 3URSHUWLHV ,WHP'HIDXOW1DPHVSDFH9DOXH
$GG 3URMHFW,WHP9LD7HPSODWH RXWSXW3DWK 7HPSODWH ,5HSRVLWRU\7HPSODWH C
0RGHO #^ 1DPHVSDFH GHIDXOW1DPHVSDFH ` C
6XFFHVV0HVVDJH $GGHG ,5HSRVLWRU\ DW ^ `C
7HPSODWH)ROGHUV 7HPSODWH)ROGHUV 3URMHFW 3URMHFW &RGH/DQJXDJH &RGH/DQJXDJH )RUFH )
RUFH
IRXQG,5HSRVLWRU\7\SH *HW 3URMHFW7\SH ,5HSRVLWRU\ 3URMHFW 3URMHFW
`
Тут как раз вызывается IRepositoryTemplate.cs.t4 при необходимости, и туда в качестве модели данных (так же, как в View) передается объект
0RGHO #^ 1DPHVSDFH GHIDXOW1DPHVSDFH ` C
А defaultNamespace был получен из свойства проекта
*HW 3URMHFW 3URMHFW 3URSHUWLHV ,WHP'HIDXOW1DPHVSDFH9DOXH
В шаблоне мы это используем (CodeTemplates/Scaffolders/IRepository/IRepositoryTemplate.cs.t4):
QDPHVSDFH 0RGHO 1DPHVSDFH !
Ок, файл создан (или найден) и идем к следующему шагу. Если всё хорошо сгеренировалось (IRXQG,5HSRVLWRU\7\SH), то добавим в этот класс несколько свойств по
шаблону ,5HSRVLWRU\,WHP7HPSODWH с параметрами:
$GG D QHZ SURSHUW\ RQ WKH 'E&RQWH[W FODVV
LIIRXQG,5HSRVLWRU\7\SH ^
SURSHUW\1DPH IRXQG0RGHO7\SH 1DPH
SURSHUW\1DPHV *HW 3OXUDOL]HG:RUG SURSHUW\1DPH
7KLV LV D 'E&RQWH[W VR ZH FDQ IUHHO\ DGG D QHZ SURSHUW\ LI WKHUH LVQ W DOUHDG\ RQH I
RU WKLV PRGHO
$GG &ODVV0HPEHU9LD7HPSODWH 1DPH SURSHUW\1DPH &RGH&ODVV IRXQG,5HSRVLWRU\7\SH 7HPSODWH ,5HSRVLWRU\,WHP7HPSODWH 0RGHO #^
(QWLW\7\SH IRXQG0RGHO7\SH
(QWLW\7\SH1DPH3OXUDOL]HG SURSHUW\1DPHV
` 6XFFHVV0HVVDJH $GGHGSURSHUW\1DPHWR LQWHUIDFHIRXQG,5HSRVLWRU\7\SH )XOO1DP
H 7HPSODWH)ROGHUV 7HPSODWH)ROGHUV 3URMHFW 3URMHFW &RGH/DQJXDJH &RGH/DQJXDJH
`
Параметры:
0RGHO #^
(QWLW\7\SH IRXQG0RGHO7\SH
(QWLW\7\SH1DPH3OXUDOL]HG SURSHUW\1DPHV
`
Кстати, обратите внимание на *HW 3OXUDOL]HG:RUG и какую роль оно сыграло в созданном шаблоне:
,4XHU\DEOH 1RWLI\! 1RWLILHV ^JHW`
Т.е. нормально сформировал множественное число, а не просто прибавлением символа ‘s’, как это было бы в сниппете.
Изучим еще эти T4Scaffolding cmdlet:
AddClassMember – добавляет кусочек кода в существующий класс:
FODVV *HW 3URMHFW7\SH +RPH&RQWUROOHU
$GG &ODVV0HPEHU FODVVSXEOLF VWULQJ 0\1HZ6WULQJ)LHOG
AddClassMemberViaTemplate – добавляем блок кода через шаблон T4:
FODVV *HW 3URMHFW7\SH +RPH&RQWUROOHU
$GG &ODVV0HPEHU9LD7HPSODWH &RGH&ODVV FODVV 7HPSODWH<RXU7HPSODWH1DPH0RGHO #^ 6RPH3DU