ASP.NET MVC Урок D. Scaffolding

в 18:53, , рубрики: .net, ASP, asp.net mvc, powershell, scaffolding, метки: , , , ,

Цель урока. Научиться использовать Scaffolding для создания прототипа проекта. Определяем и фиксируем структуру репозитория. Простая и языковая версия класса. Тестируем использование Scaffolder-а, используем «направляющие» атрибуты. Параметры для Scaffolder-а. Создание управляющих атрибутов. Полный цикл создания и управления объекта в админке.

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://t4-editor.tangible-engineering.com. Тоже пока что единственный редактор для VS2012. Ну что ж – подсветочка есть и ладно.

T4

Далее изучим, что у нас есть. Начнем с T4. Я пользовался этой ссылкой: http://www.olegsych.com/2007/12/text-template-transformation-toolkit/

Создадим новый проект, библиотеку классов LesssonProject.T4. И добавим туда HelloWorld.tt:

ASP.NET MVC Урок D. Scaffolding

Изменим немного:

<#@ template debug="true" hostSpecific="true" #>
<#@ output extension=".cs" #>
<#@ Assembly Name="System.Core" #>
<#@ Assembly Name="System.Windows.Forms" #>
<#@ import namespace="System" #>
<#@ import namespace="System.IO" #>
<#@ import namespace="System.Diagnostics" #>
<#@ import namespace="System.Linq" #>
<#@ import namespace="System.Collections" #>
<#@ import namespace="System.Collections.Generic" #> 
<#
   var greeting = "Hello, World!";
#>

// This is the output code from your template
// you only get syntax-highlighting here - not intellisense
namespace MyNameSpace 
{
	class MyGeneratedClass 
	{
		static void main (string[] args) 
		{
			System.Console.WriteLine("<#= greeting #>");
		}
	}
}
 
<#+
  // Insert any template procedures here
  void foo(){}
#>

Ок, и результатом этого будет:

// This is the output code from your template
// you only get syntax-highlighting here - not intellisense
namespace MyNameSpace 
{
	class MyGeneratedClass 
	{
		static void main (string[] args) 
		{
			System.Console.WriteLine("Hello, World!");
		}
	}
}

На самом деле .tt файл преобразуется в код, который создает конкретный класс, наследуемый от TextTransformation. Этот код запускается и генерируется файл-результат. Выглядит примерно так:

<#@ template  language="C#" #>
Hello World!

Преобразуется в:

public class GeneratedTextTransform : Microsoft.VisualStudio.TextTemplating.TextTransformation
    {
        public override string TransformText()
        {
            this.Write("Hello, World!");
            return this.GenerationEnvironment.ToString();
        }
    }

А итогом будет файл .cs:

Hello World!

Изучим блоки и синтаксис задания шаблонов, который очень похож на aspx, только вместо скобок <% %> используется <# #>. Но, так как aspx мы не изучали, то:

  • Текстовый блок – это любой не программный текст в тексте шаблона (извините за тафтологию):
    <#@ template language="C#" #>
    Hello World!
    

  • Блок операторов – это любой блок, заключенный в <# #>. Всё что внутри этого, это языковая конструкция, которая задает логику построения текста:
    <#
       var greeting = "Hello, World!";
    #>
    

  • Блок выражения – это блок, заключенный в <#= #>. Всё, что внутри этого блока, будет приведено к строке и добавлено в текст шаблона:
    System.Console.WriteLine("<#= greeting #>");
    

  • Блок функций – это блок, заключенный в <#+ #>. Все функции, объявленные в этом блоке, могут быть вызваны в шаблоне. Кроме того, сами функции могут содержать текст шаблона.
  • Директива <#@ template #> – позволяет задать характеристики класса преобразования из шаблона:
    • <#@ template language=”C#”> – задает язык класса.
    • <#@ template debug=”true”> – позволяет отладить генерацию шаблона.
    • <#@ template inherits=”MyTextTransformation”> – указывает, какой класс должен быть использован в качестве базового для класса генерации в процедуре генерации файла.
  • Директива <#@ output #> – задает расширение для генерируемого файла:
    <#@ output extension=".cs" #>
    

  • Директива <#@ import #> – добавляет использование в процедуре исполнения заданных namespace. То же самое что и using добавить (но не в результат, а при выполнении генерации текста):
    <#@ import namespace="System.Collections" #>
    

  • Директива <#@ assembly #> – добавляет объявление сборки. То же самое, что и в VisualStudio добавить Reference:
    <#@ Assembly Name="System.Core" #>
    
  • Директива <#@ include #> – добавляет некий другой шаблон в месте объявления. Это как Html.Partial():
    <#@ include file="Included.tt" #>
    
  • Директива <#@ parameter #> — добавляет параметр при формировании шаблона. Но передача его происходит настолько сложно, что пример я приводить не буду. Ссылка
  • По остальному – смотрите по ссылке.

В общем, этих знаний и знаний по Reflection вполне хватит, чтобы сгенерировать нужные нам файлы, но перейдем к проекту MvcScaffolding.

MVCScaffolding

Установим T4Scaffolding:

PM> Install-Package T4Scaffolding

Создадим папку CodeTemplates/Scaffolders/IRepository в LessonProject.Model в ней добавим файлы IRepository.ps1 (LessonProject.Model/CodeTemplates/Scaffolders/IRepository/IRepository.ps1):

[T4Scaffolding.Scaffolder(Description = "Create IRepository interface")][CmdletBinding()]
param(    
	[parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)][string]$ModelType,    
    [string]$Project,
	[string]$CodeLanguage,
	[string[]]$TemplateFolders,
	[switch]$Force = $false
)

$foundModelType = Get-ProjectType $ModelType -Project $Project -BlockUi
if (!$foundModelType) { return }

# Find the IRepository interface, or create it via a template if not already present
$foundIRepositoryType = Get-ProjectType IRepository -Project $Project -AllowMultiple
if(!$foundIRepositoryType) 
{
	#Create IRepository
	$outputPath = "IRepository"
	$defaultNamespace = (Get-Project $Project).Properties.Item("DefaultNamespace").Value
	
Add-ProjectItemViaTemplate $outputPath -Template IRepositoryTemplate `
	-Model @{ Namespace = $defaultNamespace } `
	-SuccessMessage "Added IRepository at {0}" `
	-TemplateFolders $TemplateFolders -Project $Project -CodeLanguage $CodeLanguage -Force:$Force
	
	$foundIRepositoryType = Get-ProjectType IRepository -Project $Project
}

# Add a new property on the DbContext class
if ($foundIRepositoryType) {
	$propertyName = $foundModelType.Name
	$propertyNames = Get-PluralizedWord $propertyName
	# This *is* a DbContext, so we can freely add a new property if there isn't already one for this model
	Add-ClassMemberViaTemplate -Name $propertyName -CodeClass $foundIRepositoryType -Template IRepositoryItemTemplate -Model @{
		EntityType = $foundModelType;
		EntityTypeNamePluralized = $propertyNames;
	} -SuccessMessage "Added '$propertyName' to interface '$($foundIRepositoryType.FullName)'" -TemplateFolders $TemplateFolders -Project $Project -CodeLanguage $CodeLanguage
}

return @{
	DbContextType = $foundDbContextType
}

Потом IRepositoryItemTemplate.cs.t4:

<#@ Template Language="C#" HostSpecific="True" Inherits="DynamicTransform" #>
#region <#= ((EnvDTE.CodeType)Model.EntityType).Name #>

IQueryable<<#= ((EnvDTE.CodeType)Model.EntityType).Name #>> <#= Model.EntityTypeNamePluralized #> { get; }

bool Create<#= ((EnvDTE.CodeType)Model.EntityType).Name #>(<#= ((EnvDTE.CodeType)Model.EntityType).Name #> instance);

bool Update<#= ((EnvDTE.CodeType)Model.EntityType).Name #>(<#= ((EnvDTE.CodeType)Model.EntityType).Name  #> instance);

bool Remove<#=((EnvDTE.CodeType)Model.EntityType).Name #>(int id<#= ((EnvDTE.CodeType)Model.EntityType).Name  #>);

#endregion
И IRepositoryTemplate.cs.t4:
<#@ Template Language="C#" HostSpecific="True" Inherits="DynamicTransform" #>
<#@ Output Extension="cs" #>
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

namespace <#= Model.Namespace #>
{
    public interface IRepository
    {
        IQueryable<T> GetTable<T>() where T : class;
    }
}
 

Создадим новую таблицу Notify:

Name DataType

UserIDint (foreignKey to User)
Messagenvarchar(140)
AddedDatedatetime
IsReadedbit

Перенесем в DbContext (LessonProjectDb.dbml) и сохраняем (ctrl-S):
ASP.NET MVC Урок D. Scaffolding

В Package Manager Console пишем для проекта LessonProject.Model:

PM> Scaffold IRepository Notify
Added 'Notify' to interface 'LessonProject.Model.IRepository'

Ура! Всё работает! Просто, не правда ли? Ничего не ясно? Ок, ладно разберем IRepository.ps1 по порядку:

[T4Scaffolding.Scaffolder(Description = "Create IRepository interface")][CmdletBinding()]
param(    
	[parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)][string]$ModelType,    
    [string]$Project,
	[string]$CodeLanguage,
	[string[]]$TemplateFolders,
	[switch]$Force = $false
)

Это структура объявления кода скаффолдера. Особое внимание нужно обратить на $ModelType – это имя класса, именно его мы и передаем в команде Scaffold IRepository Notify. Остальные параметры идут или по умолчанию, как Force, или по умолчанию известны, как Project, CodeLanguage.

Далее мы ищем этот класс (если мы не сохранимся, то искомый класс еще не будет записан и не будет найден):

$foundModelType = Get-ProjectType $ModelType -Project $Project -BlockUi
if (!$foundModelType) { return }

Класс найден и мы переходим к следующей части. Найти файл IRepository.cs и если его нет, то создать:

# Find the IRepository interface, or create it via a template if not already present
$foundIRepositoryType = Get-ProjectType IRepository -Project $Project -AllowMultiple
if(!$foundIRepositoryType) 
{
	#Create IRepository
	$outputPath = "IRepository"
	$defaultNamespace = (Get-Project $Project).Properties.Item("DefaultNamespace").Value
	
Add-ProjectItemViaTemplate $outputPath -Template IRepositoryTemplate `
	-Model @{ Namespace = $defaultNamespace } `
	-SuccessMessage "Added IRepository at {0}" `
	-TemplateFolders $TemplateFolders -Project $Project -CodeLanguage $CodeLanguage -Force:$Force
	
	$foundIRepositoryType = Get-ProjectType IRepository -Project $Project
}

Тут как раз вызывается IRepositoryTemplate.cs.t4 при необходимости, и туда в качестве модели данных (так же, как в View) передается объект

-Model @{ Namespace = $defaultNamespace } `

А defaultNamespace был получен из свойства проекта

Get-Project $Project).Properties.Item("DefaultNamespace").Value

В шаблоне мы это используем (CodeTemplates/Scaffolders/IRepository/IRepositoryTemplate.cs.t4):

namespace <#= Model.Namespace #>

Ок, файл создан (или найден) и идем к следующему шагу. Если всё хорошо сгеренировалось ($foundIRepositoryType), то добавим в этот класс несколько свойств по шаблону IRepositoryItemTemplate с параметрами:

# Add a new property on the DbContext class
if ($foundIRepositoryType) {
	$propertyName = $foundModelType.Name
	$propertyNames = Get-PluralizedWord $propertyName
	# This *is* a DbContext, so we can freely add a new property if there isn't already one for this model
	Add-ClassMemberViaTemplate -Name $propertyName -CodeClass $foundIRepositoryType -Template IRepositoryItemTemplate -Model @{
		EntityType = $foundModelType;
		EntityTypeNamePluralized = $propertyNames;
	} -SuccessMessage "Added '$propertyName' to interface '$($foundIRepositoryType.FullName)'" -TemplateFolders $TemplateFolders -Project $Project -CodeLanguage $CodeLanguage
}

Параметры:

-Model @{
		EntityType = $foundModelType;
		EntityTypeNamePluralized = $propertyNames;
	}

Кстати, обратите внимание на Get-PluralizedWord и какую роль оно сыграло в созданном шаблоне:

  IQueryable<Notify> Notifies { get; }

Т.е. нормально сформировал множественное число, а не просто прибавлением символа ‘s’, как это было бы в сниппете.
Изучим еще эти T4Scaffolding cmdlet:

  • Add-ClassMember – добавляет кусочек кода в существующий класс:
    $class = Get-ProjectType HomeController 
    Add-ClassMember $class "public string MyNewStringField;"
    

  • Add-ClassMemberViaTemplate – добавляем блок кода через шаблон T4:
    $class = Get-ProjectType HomeController 
    Add-ClassMemberViaTemplate -CodeClass $class -Template "YourTemplateName" -Model @{ SomeParam = "SomeValue"; AnotherParam = $false } -TemplateFolders $TemplateFolders
    

  • Add-ProjectItemViaTemplate – добавляем файл (project item) посредством шаблона:
    Add-ProjectItemViaTemplate -OutputPath "SomeFolderMyFile" -Template "YourTemplateName" -Model @{ SomeParam = "SomeValue"; AnotherParam = $false } -TemplateFolders $TemplateFolders
    

  • Get-PluralizedWord / Get-SingularizedWord – получаем множественное/единственное число слова:
    $result = Get-PluralizedWord Person    # Sets $result to "People" 
    $result = Get-SingularizedWord People    # Sets $result to "Person"
    

  • Get-PrimaryKey – получить первичный ключ из модели данных:
    $pk = Get-PrimaryKey StockItem
    

  • Get-ProjectFolder – получить класс проектной папки
    $folder = Get-ProjectFolder "ViewsShared" 
    Write-Host "The shared views folder contains $($folder.Count) items"
    

  • Get-ProjectItem – получить файл
    $file = Get-ProjectItem "ControllersHomeController.cs" 
    $file.Open() 
    $file.Activate() 
    

  • Get-ProjectLanguage – для C# проектов – возвращает cs, для VB проектов возвращает VB
    $defaultProjectLanguage = Get-ProjectLanguage 
    $otherProjectLanguage = Get-ProjectLanguage -Project SomeOtherProjectName
    

  • Get-ProjectType – получит модель класса (EnvDTE.CodeType)
    $class = Get-ProjectType HomeController 
    Add-ClassMember $class "public string MyNewStringField;"
    

  • Get-RelatedEntities – находит все связанные один-ко-многим объекты класса
    Get-RelatedEntities Product
    

  • Set-IsCheckedOut – это для работы с source-control. Т.е. если надо checkOut файл какой-то, то это можно выполнить этой командой.
    Set-IsCheckedOut "ControllersHomeController.cs"
    

Ну что? Чувствуете мощь, которая уже заменит копипастинг в ваших проектах? Но(!) Учтите, что все эти команды были реализованы тоже людьми. И, например, получение первичного ключа будет происходить только, если поле называется ID, а если оно называется PervichniyKlyuch – то, скорее всего, это не сработает. Также сильно не стоит уповать на переводы. Это скаффолдинг, т.е. черновое создание проекта, а не тончайшая настройка. Суть скаффолдинга – это создать, запустить и пойти пить чай, пока программа в автоматическом режиме сделает за вас самую противную механическую рутину.

Снова шаблоны, EnvDTE.CodeType

Вернемся к шаблонам и изучим то, что такое EnvDTE.CodeType.
CodeType – это интерфейс, к которому может быть приведена информация об классе, полученная через Get-ProjectType.
Что мы знаем про этот интерфейс. Например, про свойства:

  • Access – какой это тип, публичный, приватный и т.д..
  • Attributes – коллекции атрибутов, связанные с этим типом.
  • Bases – коллекции классов, из которых этот элемент происходит.
  • Children – возвращает коллекцию объектов, содержащихся в этом CodeType.
  • Comment – комментарий относящийся к классу. (Можно сделать автоматическую документацию).
  • DerivedTypes – Наследуемые типы. Это свойство не поддерживается в Visual C#.
  • DocComment – Документальный комментарий или атрибут, который выполняет эту роль.
  • DTE – возвращает главный объект расширения
  • EndPoint – строка в файле, с которой начинается описание этого класса.
  • FullName – полное имя, типа System.Int32
  • InfoLocation – возвращает возможности объектной модели.
  • IsCodeType – можно ли CodeType получить из этого объекта.
  • IsDerivedFrom – возвращает CodeType базового объект.
  • Kind – свойства типа объекта.
  • Language – на каком языке это написано.
  • Members – члены объекта. Вот это очень полезная функция.
  • Name – имя объекта.
  • Namespace – пространство имен объекта.
  • Parent – непосредственный родитель объекта.
  • ProjectItem – файл объекта.
  • StartPoint – строка, в которой началось описание объекта.

Есть еще методы, но мы их не используем.
Кстати, обратите внимание на EnvDTEExtensions.cs в T4Scaffolding (исходники его можно скачать отсюда: http://mvcscaffolding.codeplex.com/SourceControl/changeset/view/7cd57d172314), какие еще вспомогательные классы вам доступны.
Фух! Ну что, попробуем разложить всё по полочкам, раскрошить программно любой код, а потом объяснить компьютеру, как мы пишем программы, и идти гонять чаи.

Создадим новый проект: LessonProject.Scaffolding, и возьмем ту пару классов из первых уроков, с мечом и воином.
IWeapon.cs:

public interface IWeapon
    {
        void Kill();
    }

Bazuka.cs:

public class Bazuka : IWeapon
    {

        public void Kill()
        {
            Console.WriteLine("BIG BADABUM!");
        }
    }

Sword.cs:

public class Sword : IWeapon
    {
        public void Kill()
        {
            Console.WriteLine("Chuk-chuck");
        }
    }

Warrior.cs:

/// <summary>
/// This is LEGENDARY WARRIOR!
/// </summary>
public class Warrior
    {
        readonly IWeapon Weapon;

        public Warrior(IWeapon weapon)
        {
            this.Weapon = weapon;
        }
        
        public void Kill()
        {
            Weapon.Kill();
        }
    }

Установим T4Scaffolding:

Install-Package T4Scaffolding

Создадим простейший PowerShell (/CodeTemplates/Scaffolders/Details/Details.ps1):

[T4Scaffolding.Scaffolder(Description = "Print Details for class")][CmdletBinding()]
param(    
	[parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)][string]$ModelType,    
    [string]$Project,
	[string]$CodeLanguage,
	[string[]]$TemplateFolders,
	[switch]$Force = $false
)
$foundModelType = Get-ProjectType $ModelType -Project $Project -BlockUi
if (!$foundModelType) { return }

$outputPath = Join-Path "Details" $ModelType

Add-ProjectItemViaTemplate $outputPath -Template Details `
		-Model @{  ModelType = $foundModelType  } `
		-SuccessMessage "Yippee-ki-yay"`
		-TemplateFolders $TemplateFolders -Project $Project -CodeLanguage $CodeLanguage -Force:$Force

Заданный тип данных передаем в Details.t4 (/CodeTemplates/Scaffolders/Details/Details.cs.t4):

<#@ template language="C#"  HostSpecific="True" Inherits="DynamicTransform" debug="true" #>
<#@ assembly name="System.Data.Entity" #>
<#@ import namespace="System.Linq" #>
<#@ import namespace="EnvDTE" #>
<#@ Output Extension="txt" #>
<# 
    var modelType = (EnvDTE.CodeType)Model.ModelType; 
#>

FullName : <#= modelType.FullName #>
Name : <#= modelType.Kind #> <#= modelType.Name #>
Access : <#= modelType.Access #>
Attributes : 
<# foreach(var codeElement in modelType.Attributes) { 
	var attr = (EnvDTE.CodeAttribute)codeElement;
#> 
   <#= attr.Name #>
<# } #>

Bases : 
<# foreach(var codeElement in modelType.Bases) { 
	var @base = (EnvDTE.CodeType)codeElement;
#> 
   <#= @base.Name #>
<# } #>

Comment :  <#= modelType.Comment #>
DocComment :  <#= modelType.DocComment #>

StartPoint : Line: <#= ((EnvDTE.TextPoint)modelType.StartPoint).Line #>
EndPoint :  Line : <#= ((EnvDTE.TextPoint)modelType.EndPoint).Line #>

Members :
<# foreach(var codeElement in modelType.Members) { 
	var member = (EnvDTE.CodeElement)codeElement;
#> 
   <#= member.Kind #> <#= member.Name #>
<# } #>

Выведем для Warrior.cs

PM> Scaffold Details Warrior -Force:$true
Yippee-ki-yay
  • Имя полное имя
  • Тип модели
  • Доступ
  • Атрибуты модели
  • Базовый класс
  • Комментарий
  • Комментарий для документации
  • Строка файла, с которой началось объявление, и строка, на которой закончилось объявление
  • Имена членов класса с типом класса

Мы можем исследовать классы, использовать направляющие атрибуты и на основе этого создавать промежуточные классы, т.е. автоматизировать процессы, которые слишком рутинны для ручной работы. В то же время у нас появляется преимущество, ведь автоматически сгенерированный код содержит меньше ошибок, так как часть человеческого фактора мы убираем.

Описание скаффолдеров

Итак, я не буду тут приводить код всех используемых мною скаффолдеров, только опишу здесь их параметры для запуска. Но прежде расскажу о ManageAttribute. Эти атрибуты присваиваются тем полям, которые мы хотим в дальшейшем использовать как маркеры для генерации определенного кода. Например, атрибут LangColumn – это атрибут, указывающий на то, что данное поле является «языковым». Тем самым мы можем сгенерировать ModelView и с учетом их тоже.

  • IRepository (Model). Мы уже с ним знакомы, он создает интерфейс IRepository и вносит CRUD-методы для заданного типа:
    Scaffold IRepository ModelName
    

  • Proxy (Model). Создает Proxy partial class. Если задан параметр Lang:$true, то скаффолдер ищет языковую модель данных ModelName+”Lang” и добавляет языковые поля в partial class.
    Scaffold Proxy ModelName -Lang:$true
    

  • SqlRepository (Model). Создает реализацию CRUD-методов класса ModelName. Также имеет параметр Lang для создания приватного метода, работающего с языковыми полями
    Scaffold SqlRepository ModelName -Lang:$true
    

  • ProviderRepository (Model). Запускает три вышеперечисленных скаффолдинга за один раз
    Scaffold ProviderRepository ModelName -Lang:$true
    

  • Model (Web). Создает модель ModelNameView в Models/ViewModels и создает обработчик Automapper в Mappers/MappersCollection.cs. После этого во View-классе необходимо прописать управляющие атрибуты для создания контроллера и Index/Edit view:
    • ShowIndex – это поле будет отображено в таблице Index
    • PrimaryField – поле ID
    • CheckBox – для этого поля будет создан элемент ввода CheckBox
    • DropDownField – для этого поля будет создан элемент ввода DropDownField
    • HiddenField – скрытое поле
    • HtmlTextField – элемент ввода textarea, помеченный классом htmltext
    • RadioField – поле c радио-кнопками (на практике практически не использовалось)
    • TextAreaField – элемент ввода textarea
    • TextBoxField – обычное текстовое поле ввода

    Scaffold Model ModelName
    

  • SelectReference (Web). Создает во view-классе зависимость один-ко-многим, т.е. элемент выбора. Например, если создается город (City) с принадлежностью к штату (State), то при создании города указывается выпадающий список штатов, задающий значение StateID. Для этого необходимо использовать SelectReference, который добавит необходимый код к CityView:
    Scaffold SelectReference City State
    

  • Controller (Web). Создает контроллер для данного ModelName типа. Дополнительно генерирует и IndexEdit View. Параметрами являются:
    • Area (по умолчанию – нет), создает контроллер в определенном Area
    • Paging (по умолчанию – false), использует или не использует постраничный вывод
    • Lang (по умолчанию – false), генерирует код с использованием языковых настроек

    Scaffold Controller ModelName –Area:Admin –Paging:$true –Lang:$true
    

  • IndexViewEditView (Web). Создает просмотр списка или редактирование объекта. Дополнительные параметры — те же, что и у Controller:
    • Area (по умолчанию – нет), создает контроллер в определенном Area
    • Paging (по умолчанию – false), использует или не использует постраничный вывод
    • Lang (по умолчанию – false), генерирует код с использованием языковых настроек

    Scaffold IndexView ModelName –Area:Admin –Paging:$true –Lang:$true
    Scaffold EditView ModelName –Area:Admin –Paging:$true –Lang:$true
    

Итог

Скаффолдинг – это не панацея, но это хороший инструмент, с помощью которого можно быстро создать необходимый код. Написанные классы позволяют быстро начать управлять содержимым базы данных, и избавляют от множества ручной рутинной работы.
Действия при создании новой таблицы (объекта) будут следующие:

  • Описать таблицу(ы) с полями в БД
  • Перенести ее в DBContext.dbml
  • Запустить ProviderRepository для необходимых таблиц, убрать лишние методы
  • Запустить Model для необходимых таблиц
  • Прописать управляющие атрибуты во view-классах, убрать лишние поля
  • Создать контроллеры в админке
  • Допилить напильником сложные поля (например, загрузку файлов)

Всё это выполняется сразу на несколько таблиц, если это старт проекта или большой патч. У меня иногда генерировалось до 20-30 таблиц, это заняло около 5 минут, но без этого пришлось бы провозиться целый день.
Посмотрите на реализацию скаффолдингов, вы сможете больше понять внутренние особенности программы и ее структуру.

Все исходники находятся по адресу https://bitbucket.org/chernikov/lessons

Автор: chernikov

Источник

Поделиться

* - обязательные к заполнению поля