📜 ⬆️ ⬇️

ASP.NET MVC Lesson D. Scaffolding

The purpose of the lesson. Learn how to use Scaffolding to create a prototype project. We define and fix the repository structure. Simple and language version of the class. We test the use of Scaffolder, use the “guiding” attributes. Parameters for Scaffolder. Creating control attributes. Full cycle of creation and management of the object in the admin panel.

Scaffolding T4 for Visual Studio 2013 is not applicable.

Scaffolding Start.

In this and the next lesson we will learn what will help you to develop applications many times faster. Let's start from afar. When I did the first site, I looked at how this or that functionality could be implemented and used it in my application. Then, when I had a second project, I began to improve the functionality. I highlighted the highlights and tools that were described in previous lessons. I began to notice that I often do a lot of mechanical work, for example:

')


And since it was truly boring, I was often mistaken in one of the steps - and it was necessary to correct the banal mistakes. And I created snippets, but they solved only half of the task, but the data model, controller, index.cshtml, edit.cshtml - this was not decided.

And so I read Stephen Sanderson’s article “ Scaffold your ASP.NET MVC 3 project with the MvcScaffolding package ” and caught fire. Scaffolding was perfect for me, but it was not written for my solution. And I began to study. It was based on T4 ( Text Template Transformation Toolkit ), exactly this syntax is used in templates, but Windows PowerShell is used for pre-sample logic. Actually, we are working with PowerShell in the PackageManager Console (wow, how twisted!). I’ll dive into Windows PowerShell and T4 quite a bit, just to create a pair of scaffolders to work with the project.

So what we initially need is to install PowerGUI to work with PowerShell. There are many editors for PowerShell in VS2010. But we are working with VS2012 and so far we are not so lucky.

Ok, set. We proceed to install the editor for t4 - http://t4-editor.tangible-engineering.com . Also for now the only editor for VS2012. Well then - there is a backlight, okay.

T4

Further we will study what we have. Let's start with T4. I used this link: http://www.olegsych.com/2007/12/text-template-transformation-toolkit/

Let's create a new project, the class library LesssonProject.T4. And add HelloWorld.tt there:



Change a bit:
<#@ 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(){} #> 

Ok, and the result will be:

 // 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!"); } } } 


In fact, the .tt file is converted to code that creates a specific class that inherits from TextTransformation. This code is run and the result file is generated. It looks like this:

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


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


The result will be a .cs file:
 Hello World! 


We will study the blocks and syntax for defining templates, which is very similar to aspx, only <# #> is used instead of the brackets <%%>. But, since we did not study aspx, then:


In general, this knowledge and knowledge of Reflection is enough to generate the files we need, but let's move on to the MvcScaffolding project.

MVCScaffolding

Install T4Scaffolding:
 PM> Install-Package T4Scaffolding 


Create the CodeTemplates / Scaffolders / IRepository folder in the LessonProject.Model. In it add the IRepository.ps1 files (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 } 


Then 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; } } 


Create a new Notify table:
NameDatatype

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



Transfer to DbContext (LessonProjectDb.dbml) and save (ctrl-S):


In the Package Manager Console, we write for the LessonProject.Model project:
 PM> Scaffold IRepository Notify Added 'Notify' to interface 'LessonProject.Model.IRepository' 


Hooray! Everything is working! Simple, isn't it? Nothing clear? Ok, okay, let's sort IRepository.ps1 in order:
 [T4Scaffolding.Scaffolder(Description = "Create IRepository interface")][CmdletBinding()] param( [parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)][string]$ModelType, [string]$Project, [string]$CodeLanguage, [string[]]$TemplateFolders, [switch]$Force = $false ) 

This is the scaffolder code ad structure. Special attention should be paid to $ModelType - this is the name of the class, and that is what we pass on to the Scaffold IRepository Notify . The remaining parameters are either by default, as Force, or by default known as Project, CodeLanguage.

Then we look for this class (if we don’t save, the required class will not be recorded yet and will not be found):
 $foundModelType = Get-ProjectType $ModelType -Project $Project -BlockUi if (!$foundModelType) { return } 


The class is found and we move on to the next part. Find the file IRepository.cs and if not, then create:
 # 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 } 


This is where IRepositoryTemplate.cs.t4 is called, if necessary, and the object is transferred there as a data model (just like in the View)
 -Model @{ Namespace = $defaultNamespace } ` 


And the defaultNamespace was obtained from the project property
 Get-Project $Project).Properties.Item("DefaultNamespace").Value 


We use this in the template (CodeTemplates / Scaffolders / IRepository / IRepositoryTemplate.cs.t4):
 namespace <#= Model.Namespace #> 


Ok, the file is created (or found) and go to the next step. If everything is well-squeezed ( $foundIRepositoryType ), then add to this class a few properties using the IRepositoryItemTemplate template with parameters:

 # 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 } 

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


By the way, pay attention to Get-PluralizedWord and what role it played in the created template:
  IQueryable<Notify> Notifies { get; } 


Those. normally formed the plural, and not just adding the character 's', as it would be in the snippet.
Let us also study these T4Scaffolding cmdlet:


Well? Feel the power that already replaces copy-and-paste in your projects? But (!) Consider that all these commands were implemented by people too. And, for example, obtaining a primary key will occur only if the field is called ID, and if it is called PervichniyKlyuch, then, most likely, this will not work. Also, you should not rely on translations. This is scaffolding, i.e. draft creation, not the finest setting. The essence of scaffolding is to create, run and go to drink tea, while the program automatically makes for you the most nasty mechanical routine.

Again templates, EnvDTE.CodeType


Let's go back to the templates and learn what EnvDTE.CodeType is.
CodeType is an interface to which class information obtained through Get-ProjectType can be brought.
What we know about this interface. For example, about properties:


There are more methods, but we do not use them.
By the way, pay attention to EnvDTEExtensions.cs in T4Scaffolding (source code can be downloaded from here: http://mvcscaffolding.codeplex.com/SourceControl/changeset/view/7cd57d172314 ) what other auxiliary classes are available to you.
Whew! Well, let's try to lay everything out on the shelves, crush any code programmatically, and then explain to the computer how we write programs, and go on chasing tea.

Let's create a new project: LessonProject.Scaffolding, and take that pair of classes from the first lessons, with a sword and a warrior.
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(); } } 


Install T4Scaffolding:
 Install-Package T4Scaffolding 


Let's create the simplest 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 


The specified data type is passed to 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 #> <# } #> 


Print for Warrior.cs
 PM> Scaffold Details Warrior -Force:$true Yippee-ki-yay 




We can explore classes, use directing attributes and create intermediate classes based on this, i.e. automate processes that are too routine for manual work. At the same time, we have an advantage, because the automatically generated code contains fewer errors, since we remove part of the human factor.

Scaffolder Description

So, I will not give here the code of all the scaffolders I use, I will only describe here their parameters for launching. But before I tell you about the ManageAttribute. These attributes are assigned to the fields that we want to use later as markers for generating a specific code. For example, the LangColumn attribute is an attribute indicating that this field is “linguistic”. Thus, we can generate ModelView and taking into account them too.


Total

Scaffolding is not a panacea, but it is a good tool with which you can quickly create the necessary code. Written classes allow you to quickly begin to manage the contents of the database, and eliminate the set of manual chores.
Actions when creating a new table (object) will be as follows:


All this is done on several tables at once, if this is a project start or a large patch. I sometimes generated up to 20-30 tables, it took about 5 minutes, but without this I would have had to spend all day.
Look at the implementation of scaffoldings, you can understand more about the internal features of the program and its structure.

All sources are located at https://bitbucket.org/chernikov/lessons

Source: https://habr.com/ru/post/176097/


All Articles