Site Meter
 
 

MvcScaffolding: Creating custom scaffolders

This blog post is part of a series about the MvcScaffolding NuGet package:

  1. Introduction: Scaffold your ASP.NET MVC 3 project with the MvcScaffolding package
  2. Standard usage: Typical use cases and options
  3. One-to-Many Relationships
  4. Scaffolding Actions and Unit Tests
  5. Overriding the T4 templates
  6. This post: Creating custom scaffolders
  7. Scaffolding custom collections of files

Intro

If you only ever want to generate controllers, actions, repositories, and the other item types built into MvcScaffolding, it’s enough just to customise the T4 templates for them. But what if you want to generate something different, for example configuration files, build scripts, CSS files, JavaScript files, or something else? Or what if you want to generate combinations of multiple interrelated items?

You can create a custom scaffolder that uses arbitrary PowerShell logic. It can render T4 templates (multiple ones if you want), with the result being output:

  • As a new file in your project
  • Or, as a new code block inserted into an existing class

Or, your PowerShell logic can use Visual Studio’s “code model” API to manipulate files and code elements in other arbitrary ways.

Example: Scaffolding jQuery Plugins

Note: The rest of this post assumes you’ve already got MvcScaffolding installed. If not, see the earlier posts in this series for an introduction.

Let’s say your team uses jQuery plugins all the time. Every separate bit of client-side functionality is implemented as a separate jQuery plugin. So, to speed this up and to gain better consistency, you want to make a scaffolder that generates the outline of a jQuery plugin. To get started, run the CustomScaffolder scaffolder, giving the name jQueryPlugin for your new scaffolder:

SNAGHTML2dbba9a7_thumb1

Yes, CustomScaffolder is a scaffolder that generates other scaffolders. I find this pleasing in a recursive kind of way.

This adds a CodeTemplates folder to your project, containing files for the new scaffolder:

image_thumb5

As you can see, we’ve got two files:

  • A PowerShell script (.ps1), where we can put arbitrary logic to decide what templates get rendered and where the output goes. By default, it renders a T4 template and uses the output to create a new file called ExampleOutput in the root of your project.
  • A T4 template (.t4), i.e., the thing that the default .ps1 file renders. By default this generates a simple C#/VB class (depending on your project type).

If you want to see this working, you can run the custom scaffolder right away:

Scaffold jQueryPlugin

This will generate a new class file, ExampleOutput.cs, in the root folder of your project. That’s really just to show you how it works. We don’t really want that, so don’t run the new scaffolder yet, or if you already have done, delete ExampleOutput.cs.

Making it do something useful

Here’s the design for our jQueryPlugin scaffolder:

  • It will take two parameters: PluginName (mandatory), and HasOptions (an optional switch that modifies the JavaScript code we generate).
  • It will render a T4 template, passing those parameter values to it. The template will produce the outline of a jQuery Plugin JavaScript file.
  • The output will create a new file, /Scripts/jQuery/jQuery.pluginname.js

To actually implement this, you either need a passing familiarity with PowerShell, or at least the willingness to play around with the default script and try to modify it to suit your requirements. It’s a bit out of scope for me to try to teach PowerShell in this blog post, so let’s skip to the completed implementation. Here’s my finished jQueryPlugin.ps1 file:

[T4Scaffolding.Scaffolder(Description = "Creates a jQuery Plugin code file")][CmdletBinding()]
param(
    [parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)][string]$PluginName,
    [switch]$HasOptions,
    [string]$Project,
    [string]$CodeLanguage,
    [string[]]$TemplateFolders,
    [switch]$Force = $false
)
 
$outputPath = "Scripts\jQueryPlugins\jQuery.$PluginName.js"
 
Add-ProjectItemViaTemplate $outputPath -Template jQueryPluginTemplate `
    -Model @{ PluginName = $PluginName; HasOptions = $HasOptions.IsPresent } `
    -SuccessMessage "Added jQuery plugin at {0}" `
    -TemplateFolders $TemplateFolders -Project $Project -CodeLanguage $CodeLanguage -Force:$Force
 
Write-Host "---"
Write-Host "To reference your new jQuery plugin, copy the following script reference into your layout:"
Write-Host "<script src=`"`@Url.Content(`"~/$($outputPath.Replace("\", "/"))`")`" type=`"text/javascript`"></script>" -ForegroundColor DarkRed -BackgroundColor Gray
Write-Host "---"

I appreciate this may be a bit alien if you’ve never seen PowerShell before, but it’s not actually so bad:

  • The first few lines define metadata about the scaffolder itself – its description, and a definition of the parameters it takes (including saying which are mandatory/optional).
  • Add-ProjectItemViaTemplate is a cmdlet built into T4Scaffolding. It renders a T4 template (in this example, one called “jQueryPluginTemplate”) and creates a new file in your project containing the output (in this case, in the folder “Scripts\jQueryPlugins\”). You can pass whatever data you want from the PS1 script to the T4 template (in this example, we just pass the PluginName and HasOptions parameter values). See the end of this blog post for more info about T4Scaffolding’s built-in cmdlets.
  • Write-Host emits text into the console itself. In this example, we’re using it to supply useful information to the developer – an example <script> tag they can paste into their layout to reference the JavaScript file they just generated.

Next, we need the T4 template that defines the JavaScript code we want to generate. Here’s my finished jQueryPluginTemplate.t4 file. If you’ve written jQuery plugins before, this will probably look very familiar:

<#@ Template Language="C#" HostSpecific="True" Inherits="DynamicTransform" #>
// ----
// <#= Model.PluginName #> jQuery plugin
// ----
(function ($) {
    $.fn.<#= Model.PluginName #> = function (<# if(Model.HasOptions) { #>options<# } #>) {
<# if(Model.HasOptions) { #>
        var allOptions = $.extend({
            // TODO: Put default option values here
        }, options);
 
<# } #>
        return this.each(function () {
 
            var $this = $(this);
            // TODO: Add code here to operate on $this
 
        });
    };
})(jQuery);
Running the custom scaffolder

Now, you can invoke your custom scaffolder to generate jQuery plugins from the console. There’s full tab-completion support, so developers can auto-complete not just the name of your scaffolder (jQueryPlugin), but also the names of the custom parameters it takes.

SNAGHTML2dd907cd_thumb1[3]

This will generate a JavaScript source file at Scripts\jQueryPlugins\jQuery.reverseText.js, containing:

// ----
// reverseText jQuery plugin
// ----
(function ($) {
    $.fn.reverseText = function (options) {
        var allOptions = $.extend({
            // TODO: Put default option values here
        }, options);
 
        return this.each(function () {
 
            var $this = $(this);
            // TODO: Add code here to operate on $this
 
        });
    };
})(jQuery);

Once you’ve referenced this script file (e.g., by copy & pasting the example <script> tag emitted to the console), you can use your new plugin to operate on the HTML elements that match a jQuery selector, e.g.:

$(".someClass").reverseText();
A runnable example

Download the demo project, which contains the jQueryPlugin custom scaffolder. You can run it from the Package Manager console as follows:

Scaffold jQueryPlugin somePluginName             # Like this...
Scaffold jQueryPlugin somePluginName –HasOptions # ... or like this

Useful Scaffolding Cmdlets

In the preceding example, I demonstrated the use of Add-ProjectItemViaTemplate, a PowerShell cmdlet included in the core T4Scaffolding package (installed automatically when you install MvcScaffolding).

There are many other useful cmdlets included in T4Scaffolding that you can use from your custom scaffolders, for example to:

  • Render T4 templates
  • Inspect and modify files in your solution’s projects
  • Inspect and modify code elements in your classes
  • Detect primary keys and 1:many relations on entity classes
  • Interact with source control bindings
  • … or merely to pluralise words.

For a list of examples, see this wiki page on CodePlex.

Conclusion

With a bit of PowerShell-fu (or a bit of copy-paste-existing-examples-fu), you can make your own scaffolder that:

  • Takes a custom set of parameters
  • Renders a custom set of T4 templates
  • Sends the output to a custom set of locations in your project or in existing classes

Once you’ve made it, it’s easy to share with colleagues: just commit your /CodeTemplates folder into source control, and then anyone working on your project will be able to run your scaffolder

In the next post, I’ll present a slightly bigger example that does output multiple files, and produces a different type of controller with an Ajax-powered grid in its view.

18 Responses to MvcScaffolding: Creating custom scaffolders

  1. This is pretty cool. I was wondering how do you inspect a class already in your projects and the properties that it has?

    I am thinking of a scaffolder that inspects a class that you pass it and then builds client-side templates for you based on what client-side library you are using : JQuery Templates, JQote, Mustache.js, or Pure.

    It would also be great if this scaffolder could take into account whether your template will be a one off template or part of an array (http://haacked.com/archive/2008/10/23/model-binding-to-a-list.aspx).

    Can you give any advice on building something like this? Methods I should be aware of or caveats?

  2. Steve

    Hi Khalid – the blog post I’m publishing tomorrow (custom Ajax-powered controller) will show an example of doing that. It generates JavaScript code for each property on an existing model class.

  3. sweet, can’t wait to see it.

  4. Great stuff – knew you were building up to something tasty. Can’t wait for tomorrow.

  5. James Culbertson

    Thanks, Steve, that’s very nice indeed! I may have missed in an earlier episode, but is there a way to provide help for discovery about the available templates and parameters available for the individual one. For example entering “Scaffold” with no args would show the available scaffolds and for example “Scaffold CustomScaffolder -h” would show the args and options for that particular scaffolder without producing output.

    Cheers,
    James

  6. Pingback: The Morning Brew - Chris Alcock » The Morning Brew #830

  7. MvcScaffolding is cool; however, we found it easier to write powershell scripts to do the work. The plus side is using WPK we where able to provide a gui for all the possible arguments. The files get created in the right directory. Another plus is the NUnit test are built at the same time.

  8. LaNika

    Great stuff. However, looks like powershell scripts must be digitally signed. Not sure if there is a way to avoid.

  9. Chris Kinsman

    Looks like with the MVC3 Tooling release including MvcScaffolding customscaffolder is missing…

  10. Emile

    Scaffolding is surely the way to go. I am happy see that .NET is adopting the ruby on rails development flow. I would suggest that we add an feature for migrations as well, so that we can up and down migrate our data safely and alter the the database structure aswell. I know EF has a sort of magic approach on picking up structure changes, but i thing that migrations have proven itself pretty much over the years.

  11. What is the best way to determine the view types (Razor or WebForms) according to the host solution? Is there a PowerShell command for this?

  12. Farooq

    Great series of articles! I was wondering how can a project and/or solution file be generated first and then output project attribute be used to place the generated output in the newly created project. Can you point to or show any example please?

  13. Am using NuGet 1.4 and getting issues when creating my own Scaffolders. I can’t work out what is going wrong. Even copy/paste of the examples doesn’t work and the download won’t work in VS2010 as it isn’t signed?

  14. John

    So the custom scaffolder is created in my project, right?

    How do I make a custom scaffolder that is available for any Visual Studio project?

  15. Betty

    @John – Turn it into a NuGet package.

  16. It’s the best time to make some plans for the future and it’s time to be happy. I’ve learn this submit and if I may just I want to counsel you some interesting things or advice. Perhaps you can write next articles relating to this article. I wish to read more issues about it!

  17. Steven,
    Where i can find the default powershell file that cames with Visual Studio MVC3. I’m having problems with an entity object that references to another entity object. The default powershell works, but the powershell that i created did not. The problem is the command “property.IsForeignKey” that always return false.

  18. This looks pretty awesome. Any chance of getting some scaffolding to automatically generate KnockoutJS models from POCO classes?