MvcScaffolding: Scaffolding custom collections of files
This blog post is part of a series about the MvcScaffolding NuGet package:
- Introduction: Scaffold your ASP.NET MVC 3 project with the MvcScaffolding package
- Standard usage: Typical use cases and options
- One-to-Many Relationships
- Scaffolding Actions and Unit Tests
- Overriding the T4 templates
- Creating custom scaffolders
- This post: Scaffolding custom collections of files
Previously you’ve seen how to modify the output from existing scaffolders (by overriding their T4 templates) and how to create custom scaffolders that take arbitrary parameters and render new templates of their own.
But what if, instead of generating just a single file, you want to generate multiple files that all work together? Well, it’s not surprising that you can do so.
- You can put multiple Add-ProjectItemViaTemplate commands in your PowerShell file. You can generate as many different files, using as many different templates, as you want.
- You can invoke one scaffolder from another. For example your custom scaffolder logic can contain “Scaffold Controller” or “Scaffold View” instructions to generate controllers and views using the built-in logic.
In this final post in the current MvcScaffolding series, I’ll show how some of these ideas can be tied together to build something useful.
Example: An Ajax-powered Controller
I showed an example of doing this in my MvcConf talk (video here) a couple of months ago, when I built a custom ControllerWithAjaxGrid scaffolder. This custom scaffolder produces a controller (obviously), but instead of having the usual index/edit/details/delete views, it just has a single view that contains an Ajax-powered grid from which the user can view, add, edit, and delete records. Videos are hard to reuse code from, so for your convenience I’ll reproduce the code in this blog post.
To see it in action, download the demo project, and then scaffold an Ajax-grid controller for the included StockItem model by executing “Scaffold ControllerWithAjaxGrid StockItem” in the console:
Run the controller by launching the application and navigating to /StockItems:
It uses jqGrid to do all the client-side stuff. By the way, if you’re wondering how to make jqGrid do different things like sorting or filtering, don’t ask me, I have no idea – talk to the experts! Let’s keep this post focused on the scaffolding side of things.
How ControllerWithAjaxGrid Works
I won’t list all the code for ControllerWithAjaxGrid and its template – you can download the runnable demo project for that. As an outline of how it works, ControllerWithAjaxGrid.ps1 contains the following instruction to create the controller itself:
Scaffold MvcScaffolding.Controller $ModelType -Project $Project ` -CodeLanguage $CodeLanguage -OverrideTemplateFolders $TemplateFolders ` -NoChildItems -Force:$Force
This inherits all the normal controller-scaffolding logic (e.g., knowing which folder to output the class file into) from MvcScaffolding.Controller, and merely overrides location where the T4 template is found (using –OverrideTemplateFolder). I was then able to put a ControllerWithContext.cs.t4 file in my custom scaffolder’s folder and hence override the code that gets generated. My custom controller template returns JsonResult data to support the Ajax grid. For example, here’s the template for its GetData action:
public JsonResult GridData(int rows, int page) { var count = _context.<#= modelNamePlural #>.Count(); var pageData = _context.<#= modelNamePlural #>.OrderBy(x => x.<#= Model.PrimaryKey #>).Skip((page - 1) * rows).Take(rows); return Json(new { page = page, records = count, rows = pageData, total = Math.Ceiling((decimal)count / rows) }, JsonRequestBehavior.AllowGet); }
Similarly, my custom controller doesn’t need all the usual views – it only needs one that contains the JavaScript code to display the grid. I generate that view using a “Scaffold View” command in ControllerWithAjaxGrid.ps1, again using the –OverrideTemplateFolders parameter to make the View scaffolder load the T4 template from ControllerWithAjaxGrid’s folder.
One interesting thing about the view template is that it gets a list of all the properties on your model class:
<# var properties = viewDataType.VisibleMembers().OfType<codeProperty>(); #>
… then later uses this to generate some JavaScript code relating to each property:
<script type="text/javascript"> jQuery("#ajaxGrid").jqGrid({ ... colNames: [<#= string.Join(", ", properties.Select(prop => "'" + prop.Name + "'")) #>], ... }); </script>
Finally, my custom controller needs to be sure that there is actually a data context class that it can use for data access. This is easy to arrange: I just put the following command in ControllerWithAjaxGrid.ps1:
Scaffold DbContext $ModelType -DbContextType ((Get-Project $Project).Name + "Context")
You can see I’m using the convention that the data context class should be called ProjectNameContext, but you could change that if you wanted.
For the full code including the T4 templates, download the demo project.
Conclusion
You could use the same techniques to create other custom controller types with different combinations of views, data access classes, and so on.
If you’re unfamiliar with PowerShell then it might take a bit of learning, but once you’ve got a custom scaffolder to suit your project’s requirements, you can commit your /CodeTemplates folder into source control, and then everyone on your team can quickly generate controllers in the style you’ve defined, supplying whatever custom parameters you’ve declared.
For more information about creating custom controllers, see the tutorial in my previous blog post and the list of useful cmdlets for custom scaffolder scripts.