Chapter 4 overviewed the MVC 4 use of scaffolded views, which make it easy to create the controller and views to support create, read, update, and delete functionality just by setting options in the Add Controller dialog. As noted in Chapter 4, this scaffolding system is extensible. This section describes a few approaches for extending the default scaffolding experience.
The default scaffolding provided by MVC is powered by T4 templates (T4 is a code-generation engine integrated with Visual Studio.) Assuming your Visual Studio installed directory was C:Program Files (x86)Microsoft Visual Studio 11.0, you would find these templates in the following locations:
MVC first looks for a CodeTemplates folder in your project, so if you want to customize new controllers, you can copy the CodeTemplates folder directly into the root of your project and add your own T4 templates.
Visual Studio will complain with the following message:
Compiling transformation: The type or namespace name ‘MvcTextTemplateHost’ could not be found (are you missing a using directive or an assembly reference?)
The reason for this is that when adding a T4 file to a project, Visual Studio sets the value of the Custom Tool property for each template to the value TextTemplatingFileGenerator. For a standalone T4 file, this is what you want. But in the case of your view scaffolds, this value is not correct. To fix this issue, select all the T4 files and clear the Custom Tool property in the Properties window, as shown in Figure 15.9.
Better yet, you can install the Mvc4CodeTemplatesCSharp NuGet package (or Mvc4CodeTemplatesVB for Visual Basic) into your project. This copies the templates into your project; it also sets the build action correctly for these files so Visual Studio doesn't try to run them when you open them.
The Add View dialog will now give preference to the view scaffold T4 templates in your project over the default ones of the same name. You can also give some templates a new name. The Add View dialog will show your new templates as options in the Scaffold Template drop-down list.
While the T4 approach in the previous section works, the scaffolding experience in ASP.NET MVC 4 is dramatically improved by the MvcScaffolding NuGet package.
Install-Package MvcScaffolding
This package is written by a member of the ASP.NET team, although it's not a Microsoft product or officially supported. It adds several great scaffolding features:
For precisely that last reason, we're not going to document MvcScaffolding in great detail here — it would very likely be out of date by the time you read this. We'll give you an overview of how it works, and then point you toward web references so you can keep up with future updates.
The MvcScaffolding package adds two new options to the Add Controller dialog, as shown in Figure 15.10.
To use the repository template, add a new controller and select the “MvcScaffolding: Controller with read/write actions and views, using repositories” template, as shown in Figure 15.11.
This example replaces the existing StoreManagerController in the MVC Music Store application with a new controller (and views). Instead of including Entity Framework data access code in the controller, as shown in the example in Chapter 4, this controller abstracts the data access code to a separate AlbumRepository class. The code for this class is shown as follows.
using System; using System.Collections.Generic; using System.Data; using System.Data.Entity; using System.Linq; using System.Linq.Expressions; using System.Web; namespace MvcMusicStore.Models { public class AlbumRepository : IAlbumRepository { MusicStoreEntities context = new MusicStoreEntities(); public IQueryable<Album> All { get { return context.Albums; } } public IQueryable<Album> AllIncluding( params Expression<Func<Album, object>>[] includeProperties) { IQueryable<Album> query = context.Albums; foreach (var includeProperty in includeProperties) { query = query.Include(includeProperty); } return query; } public Album Find(int id) { return context.Albums.Find(id); } public void InsertOrUpdate(Album album) { if (album.AlbumId == default(int)) { // New entity context.Albums.Add(album); } else { // Existing entity context.Entry(album).State = EntityState.Modified; } } public void Delete(int id) { var album = context.Albums.Find(id); context.Albums.Remove(album); } public void Save() { context.SaveChanges(); } } public interface IAlbumRepository { IQueryable<Album> All { get; } IQueryable<Album> AllIncluding( params Expression<Func<Album, object>>[] includeProperties); Album Find(int id); void InsertOrUpdate(Album album); void Delete(int id); void Save(); } }
Separating the data access logic from the controller code provides a number of benefits. It's easier to test the controller code (as explained in more detail in Chapter 12, in the section titled “Keep Business Logic out of Your Controllers”). Additionally, it's now possible to reuse the repository code elsewhere in your project.
The MvcScaffolding system uses scaffolders to generate code. You can create your own scaffolders, and conveniently (but slightly funny in a mind-bending way) the easiest way to get started with the code for your custom scaffolders is to generate it — using CustomScaffolder, a scaffolder included in MvcScaffolding, of course.
Creating a new scaffolder to handle a new controller scenario, for instance, is as simple as typing the following in the package manager console:
Scaffold CustomScaffolder AwesomeController
This adds the required files for the AwesomeController scaffolder to a new folder in your project, CodeTemplatesScaffoldersAwesomeController. Of course it's up to you to edit the generated code for this scaffolder, but everything's set up for you so you can just focus on the code that makes your scaffolder unique.
As promised, we've kept this discussion at a pretty high level because it's subject to change. The best source of information on MvcScaffolding at the time of this writing is found on Steven Sanderson's blog (as he is the primary author of MvcScaffolding): http://blog.stevensanderson.com/?s=scaffolding.