Leverage T4Scaffolding for WCF Web API

MvcScaffolding is a tremendous productivity improvement for MVC developers. Earlier this year, Steve Sanderson posted the definitive 7-part series on MvcScaffolding which is a must-read for anyone serious about doing scaffolding. The best part about the scaffolding infrastructure is that MvcScaffolding is actually built on top of another package called T4Scaffolding. T4Scaffolding can be used as the basis for any scaffolding – not just MVC projects. To that end, we can enjoy the same power of MvcScaffolding in non-MVC projects like the WCF Web API.

Recently I did a post that showed how easy it was to get up and running with Web API by leveraging NuGet. This consisted of a NuGet package which added the appropriate references and provided starter code for a basic HTTP CRUD service (I included a 6-minute screencast showing it in action). However, there are several aspects of the NuGet-only approach that are not ideal:

  • State was held by a static in-memory dictionary which, while good for a demo, you have to replace.
  • It created a dummy "MyModelType" class where you had to then rely on Visual Studio refactoring tools to perform a "rename" operation everywhere.
  • You have to manually move the model type to its own code file.
  • You have to manually rename the Service class.
  • You have to manually specify the route you want.
  • It only created a single service – so if you want to create a second service, it was a copy/paste exercise.

By leveraging T4Scaffolding, we can address all of these issues. I'll start with the end result and then discuss the implementation.

Web API Scaffolding

I have created a NuGet package called WebApi.Scaffolding. You can start with the "Empty ASP.NET Web Application" project if you want to follow this step by step. The first step is to execute the install for this package:

PM> Install-Package WebApi.Scaffolding

This installs the package along with a few dependent NuGet packages (which I'll discuss more later). Like MvcScaffolding, this doesn't add any binary references to your project – instead it adds PowerShell commands to the Package Manager console. We can immediately scaffold our first Web API HTTP service:

PM> Scaffold WebApiService -ResourceName ContactResource -ServiceName ContactService -DbContextType ContactsDbContext

This results in 5 files being created at once:

webapi-solexpl

And just like that, all 6 of our "less than ideal" aspects from before are addressed. Let's take a closer look at what was generated.

To get up and running quickly, I also highly recommend you install SQL Server Compact Edition:

PM> Install-Package EntityFramework.SqlServerCompact

MvcScaffolding revolves around the Model. The controller, data access, and views are generated from it. Web API Scaffolding works in a similar way. That is, we start with a resource ("ContactResource" in this example) and the service and data access is generated from it. The generated resource is very simple:

public class ContactResource
{
    public int Id { get; set; }
 
    //TODO: Add other properties below
}

By convention it just has an Id and we can add in any other properties we want without affecting the service or data access. More importantly if you already have a type that you want to expose, you can just specify the existing type for the ModelResource and it will use that and will not overwrite the class.

The next thing is the service class:

[ServiceContract]
public class ContactService
{
    private readonly IContactResourceRepository contactresourceRepository;
 
    // If you are using Dependency Injection, you can delete the following constructor
    public ContactService () : this(new ContactResourceRepository())
    {
    }
 
    public ContactService (IContactResourceRepository contactresourceRepository)
    {
        this.contactresourceRepository = contactresourceRepository;
    }
 
    [WebGet(UriTemplate = "{id}")]
    public HttpResponseMessage<ContactResource> Get(int id)
    {
        var item = this.contactresourceRepository.Find(id);
        if (item == null)
        {
            var notFoundResponse = new HttpResponseMessage();
            notFoundResponse.StatusCode = HttpStatusCode.NotFound;
            notFoundResponse.Content = new StringContent("Item not found");
            throw new HttpResponseException(notFoundResponse);
        }
        var response = new HttpResponseMessage<ContactResource>(item);
 
        // set it to expire in 5 minutes
        response.Content.Headers.Expires = new DateTimeOffset(DateTime.Now.AddSeconds(30));
        return response;
    }
 
    [WebInvoke(UriTemplate = "", Method = "POST")]
    public HttpResponseMessage<ContactResource> Post(ContactResource item)
    {
        this.contactresourceRepository.InsertOrUpdate(item);
        this.contactresourceRepository.Save();
 
        var response = new HttpResponseMessage<ContactResource>(item);
        response.StatusCode = HttpStatusCode.Created;
        response.Headers.Location = new Uri("Contacts/" + item.Id, UriKind.Relative);
        return response;
    }
 
    [WebInvoke(UriTemplate = "{id}", Method = "PUT")]
    public ContactResource Put(int id, ContactResource item)
    {
        this.contactresourceRepository.InsertOrUpdate(item);
        this.contactresourceRepository.Save();
        return item;
    }
 
    [WebInvoke(UriTemplate = "{id}", Method = "DELETE")]
    public HttpResponseMessage Delete(int id)
    {
        this.contactresourceRepository.Delete(id);
        this.contactresourceRepository.Save();
        return new HttpResponseMessage(HttpStatusCode.NoContent, "No Content");
    }

}

Notice how it's using a repository for data access rather than an in-memory static dictionary. This repository encapsulates an Entity Framework 4.1 (Code First) data context. All this is made possible because T4Scaffolding makes this available out of the box.  Finally, we have the ContactServiceStart.cs file:

using System;
using System.Web.Routing;
using Microsoft.ApplicationServer.Http.Activation;
using Microsoft.ApplicationServer.Http.Description;
 
[assembly: WebActivator.PreApplicationStartMethod(typeof(WebApiScaffoldingDemo.App_Start.ContactServiceStart), "Start")]
 
namespace WebApiScaffoldingDemo.App_Start {
    public static class ContactServiceStart {
        public static void Start() {
            RouteTable.Routes.MapServiceRoute<ContactService>("contacts");
        }
    }
}

Notice that we are using the WebActivator library which automatically invokes our initialization code during application start up for an ASP.NET application. Also notice that the route of "contacts" was created automatically by pluralizing the model type (and removing the word "Resource" from the end). All of this was generated automatically with no need to customize. Additionally, if we want to create another service, we just scaffold again – no copy/paste required.

One thing you might have noticed in the service code above, is that there are 2 constructors and line #6 has the comment stating that you can remove the one with the default constructor if you're using Dependency Injection with an IoC container. Fortunately, this is now easily supported in the WCF Web API. But if I know that I use IoC for my entire application, I don't want to have to delete the default constructor every single time. Wouldn't it be nice if I could tell the Scaffolder that I'm using IoC? The good news is that you can:

PM> Scaffold WebApiService -ResourceName ContactResource -ServiceName ContactService -DbContextType ContactsDbContext -UseIoC -Force

There are two things to notice here. First, I've added a –Force switch here because by default it will not overwrite the files that Scaffolding has already created unless you explicitly indicate that it should. Second, I've added a –UseIoC switch on the end. This tells the code to generate the service without a default constructor and well as change the initialization code in the ContactServiceStart class. Now the initialization code looks like this:

[assembly: WebActivator.PreApplicationStartMethod(typeof(WebApiScaffoldingDemo.App_Start.ContactServiceStart), "Start")]
 
namespace WebApiScaffoldingDemo.App_Start {
    public static class ContactServiceStart {
        public static void Start() {
            var iocContainer = IoC.Initialize();
            var config = HttpHostConfiguration.Create()
                .SetResourceFactory(new IoCResourceFactory(iocContainer));
            RouteTable.Routes.MapServiceRoute<ContactService>("contacts", config);
        }
    }
}

The Web API Scaffolder has generated the code necessary to use IoC. It has also included two more files in a new folder called DependencyResolution:

WebApiScaffold-solexpl-depres

These files include the initialization of the IoC container as well as a Web API Resource Factory that uses it. The Web API Scaffolder has a dependency on StructureMap so the StructureMap NuGet package is a dependency that is automatically added when you add the WebApi.Scaffolding NuGet package. Of course, you can always replace this with your IoC of choice.

Building Web API Scaffolding

Now that we've seen what WebApi.Scaffolding does, let's look closer into how it does it. The structure of the solution for WebApi.Scaffolding looks like this:

WebApiScaffold-nuget-solexpl

The heart of the scaffolding solution is the WebApiService.ps1 file. This is the PowerShell commandlet that is invoked by the scaffolding commands that are executed from the console. The first part of this commandlet is the declaration:

[T4Scaffolding.Scaffolder(Description = "This Scaffolder create a WCF Web API service")][CmdletBinding()]
param(
    [parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)][string]$ServiceName,
    [parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)][string]$ResourceName,
    [string]$DbContextType,
    [string]$Project,
    [string]$CodeLanguage,
    [string[]]$TemplateFolders,
    [switch]$Force = $false,
    [switch]$UseIoC = $false
)

You can see the parameters I used earlier (i.e., ResourceName and ServiceName) as well as the switches (i.e., Force and UseIoC). The next thing do is to utilize the Add-ProjectItemViaTemplate commandlet that comes with T4Scaffolding:

   1:  Add-ProjectItemViaTemplate "Resources$ResourceName" -Template WebApiResourceTemplate `

   2:      -Model @{ Namespace = $namespace; ResourceName = $ResourceName } `

   3:      -SuccessMessage "Added WebApiService output at {0}" `

   4:      -TemplateFolders $TemplateFolders -Project $Project -CodeLanguage $CodeLanguage -Force:$false

This commandlet really does some heavy lifting for us by invoking the T4 template specified (WebApiResourceTemplate) and adding the result to our Visual Studio solution at the location specified. The other thing to notice is the –Model parameter that I am passing which consists of 2 properties (Namespace and ResourceName) that I am dynamically specifying. This allows me to use these properties in my T4 template as shown on line #8 and #10:

<#@ 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 #>.Resources
{
    public class <#= Model.ResourceName #>
    {
        public int Id { get; set; }
 
        //TODO: Add other properties below
    }
}

I can add my template for the Service class in a similar way. Next, I create the repository by leveraging the repository scaffolder that comes with T4Scaffolding:

   Scaffold Repository -ModelType $ResourceName -DbContextType $DbContextType -Project $Project -CodeLanguage $CodeLanguage -Force:$Force -BlockUi

This command not only creates the repository for me but also the Entity Framework DbContext – this all comes built-in with T4Scaffolding!

The next thing I want to do is to create my initialization code. You'll recall it created a route for me like this:

RouteTable.Routes.MapServiceRoute<ContactService>("contacts");

We specified "ContactResource" for the –ResourceType but I don't want the word "Resource" in my route and I also want the pluralized word for the URI. Once again, T4Scaffolding comes in handy because it comes with a commandlet called Get-PluralizedWord which we can use like this:

$route = Get-PluralizedWord $ResourceName.Replace("Resource", "").ToLower()
$appStartName = "App_Start$ServiceName" + "Start"
Add-ProjectItemViaTemplate $appStartName -Template WebApiAppStartTemplate `
    -Model @{
        Namespace = $namespace;
        ServiceName = $ServiceName;
        ServiceRoute = $route;
        UseIoC = [Boolean]$UseIoC;
    } -SuccessMessage "Added WebApiService output at {0}" `
    -TemplateFolders $TemplateFolders -Project $Project -CodeLanguage $CodeLanguage -Force:$Force

The $route variable on line #1 now is "contacts" which gets passed into the T4 template for the ServiceRoute on line #7.

Finally, I have the conditional IF statement that determines whether or not to add the DependencyResolution infrastructure depending on if the –UseIoC switch was specified:

if ($UseIoC) {
    Add-ProjectItemViaTemplate "DependencyResolutionIoCResourceFactory" -Template WebApiIoCResourceFactoryTemplate `
        -Model @{
            Namespace = $namespace;
        } -SuccessMessage "Added WebApiService output at {0}" `
        -TemplateFolders $TemplateFolders -Project $Project -CodeLanguage $CodeLanguage -Force:$Force
 
    Add-ProjectItemViaTemplate "DependencyResolutionIoC" -Template WebApiIoCTemplate `
        -Model @{
            Namespace = $namespace;
        } -SuccessMessage "Added WebApiService output at {0}" `
        -TemplateFolders $TemplateFolders -Project $Project -CodeLanguage $CodeLanguage -Force:$Force
}

Customizing Web API Scaffolding

Up to this point we've seen what Web API Scaffolding does and how it gets built. But what if you want to customize it in your own project? For example, let's say you know that you always use a certain Media Type Formatter and you want the initialization code to always be generated to specify your media type formatter. For example, let's say I want to use HAL for my media type and I have a HalMediaTypeFormatter (described here in a previous post). All you have to do is to run this command in your project:

PM> Scaffold CustomTemplate WebApiService WebApiAppStartTemplate

This will create a "CodeTemplates" folder in your solution for the WebApiService Scaffolder with the WebApiAppStartTemplate:

webapi-custempl

You can then modify the T4 template for WebApiAppStartTemplate to add the formatter as you see on line #8 below:

namespace <#= Model.Namespace #>.App_Start {
    public static class <#= Model.ServiceName #>Start {
        public static void Start() {
<# if (Model.UseIoC) { #>
            var iocContainer = IoC.Initialize();
            var config = HttpHostConfiguration.Create()
                .SetResourceFactory(new IoCResourceFactory(iocContainer))
                .AddFormatters(new HalMediaTypeFormatter());
            RouteTable.Routes.MapServiceRoute<<#= Model.ServiceName #>>("<#= Model.ServiceRoute #>", config);
<# } else { #>
            RouteTable.Routes.MapServiceRoute<<#= Model.ServiceName #>>("<#= Model.ServiceRoute #>");
<# } #>
        }
    }
}

Next time you run the Scaffold WebApiService… command, it will automatically pick up the changes in your locally customized T4 template.

T4Scaffolding gives you a HUGE amount of power and control when building your own solutions. It is well worth your time to familiarize yourself with the tools available to you and you will be amazed to find new ways to increase your productivity that you hadn't before thought of. The sky is the limit for the different types of "recipes" you can create for yourself.

The WebApi.Scaffolding package is available on NuGet. The complete source code can be found on CodePlex.

Tweet Post Share Update Email RSS