Turn your Razor helpers into reusable libraries

Note: the generator has evolved since this post. Although the post is still worth reading, please go to https://razorgenerator.codeplex.com/ for the most up to date doc.

The first blog post I ever wrote was titled “Turning an ascx user control into a redistributable custom control”.  It was almost exactly five years ago, and it still gets a lot of hits today.  And interestingly, this new blog post is about solving essentially the same problem, but with a much nicer Razor based solution than was available at the time.

The general issue we’re trying to solve is to encapsulate reusable pieces of UI.  Unfortunately, this has typically meant choosing between two approaches, each having their pros and cons (this mirrors the intro from my old post):

  1. Custom code in a library project: this makes it easy to produce a binary that can be used in multiple projects without having to keep source files around.  But on the downside, it’s painful to author rendering logic without a view engine language
  2. Using a non-precompiled markup file: in the WebForms world, that meant an ascx file, while in the Razor world, it means a .cshtml file.  This makes it easy to write rendering logic using ascx or Razor syntax.  But the drawback is that it’s hard to turn into a reusable library.

Here, I will show you have you can have the best of both worlds in the Razor world: write your helpers using the powerful Razor declarative helpers syntax, while still being able to build them into a reusable library.

 

The quick ‘getting started’ guide

 

If you don’t care about the details of how this works and just want to use it, here is what you need to know to run it.

  • Go to the VS Extension Gallery and install the Razor Single File Generator.

image

 

  • Here you may need to restart VS
  • In a Library project, create a Razor file (.cshtml extension)
  • Under its properties, set the Build Action to None, and set the custom tool to RazorClassGenerator

image

  • Define various @helper methods in the cshtml file.  When you save it, it’ll produce a nested .cs file, e.g.

image

  • Reference the library from your Razor view (in an MVC3 or Web Pages app) and use the helpers!

And if you’re looking for the source code, it’s all on CodePlex: https://razorgenerator.codeplex.com/

 

What are Razor declarative helpers?

 

Scottgu introduced the concept in his Razor post, under the “Declarative HTML Helpers” section.  Here is an example:

 @helper WriteList(string[] items) {
    <ul>
        @foreach (var s in items) {
            <li>
                @s
            </li>
        }
    </ul>
}

Here, we are using the powerful Razor syntax to define what our helper will output.  The code in the method is basically the same thing as you write in regular Razor rendering logic, but the fact that it’s inside an @helper method turns it into a declarative helper.

Normally, those @helper methods must live either in your view itself, or in a .cshtml file in App_Code.  When it’s in the view itself, it’s only usable within that one view, while when it’s in App_Code, it’s usable from anywhere in your app.  But in either case, it really isn’t very reusable in the sense that you can’t easily turn it into a library of helpers that you can use in any app without having to carry the .cshtml file.

 

A VS Single File Generator to the rescue

 

If you’ve ever used T4 (which I’ve blogged quite a bit about), then you pretty much know what a Single File Generator is.  It’s something that you can attach to a file in your project, such that it generates another file underneath it.

In this case, the file we attach a SingleFileGenerator to is the .cshtml file, and what it generates is the source code that the Razor engine produces from it.

Writing a VS Single File Generator may seem scary, but luckily there is a good sample in the SDK: https://code.msdn.microsoft.com/sfgdd.  In fact, most of the code in my Razor generator is directly copied from this.  The only place that has interesting code that’s specific to Razor is the RazorClassGenerator.GenerateCode() method.  Here is the key code (simplified for brevity):

 // Determine the project-relative path
string projectRelativePath = InputFilePath.Substring(appRoot.Length);

// Turn it into a virtual path by prepending ~ and fixing it up
string virtualPath = VirtualPathUtility.ToAppRelative("~" + projectRelativePath);

// Create the same type of Razor host that's used to process Razor files in App_Code
var host = new WebCodeRazorHost(virtualPath, InputFilePath);

// Set the namespace to be the same as what's used by default for regular .cs files
host.DefaultNamespace = FileNameSpace;

// Create a Razor engine nad pass it our host
var engine = new RazorTemplateEngine(host);

// Generate code
GeneratorResults results = null;
using (TextReader reader = new StringReader(inputFileContent)) {
    results = engine.GenerateCode(reader);
}

// Then results.GeneratedCode has the CodeDom CodeCompileUnit that the generator needs

So it’s all pretty simple.  In essence, all it does is give the content of the Razor file to the Razor engine and asks it to generate the right code for it.

 

Possible areas of improvement

 

The most obvious pain point when using this is that you need to manually set the custom tool to RazorClassGenerator.  It should be relatively easy to add behavior to the VSIX that would add a right click option on .cshtml files that would set this up.

Another potentially very cool thing is to not only use this to precompile @helpers, but also real Views.  This is trickier because it requires some logic that will allow the view engine to find the precompiled Views, but it can certainly be done.