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

T4

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

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



Изменим немного:
<#@ 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):


В 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 "Some\Folder\MyFile" -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 "Views\Shared" 
    Write-Host "The shared views folder contains $($folder.Count) items"
    

  • Get-ProjectItem – получить файл
    $file = Get-ProjectItem "Controllers\HomeController.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 "Controllers\HomeController.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 типа. Дополнительно генерирует и Index\Edit View. Параметрами являются:
    • Area (по умолчанию – нет), создает контроллер в определенном Area
    • Paging (по умолчанию – false), использует или не использует постраничный вывод
    • Lang (по умолчанию – false), генерирует код с использованием языковых настроек

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

  • IndexView\EditView (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