Thursday, 3 October 2013

Executing Razor Views outside of a web application with NonHttpRunTimeRazorSupport

NonHttpRunTimeRazorSupport (catchy) is a small library that allows you to use ASP.Net MVC Razor views to render content outside of the context of a running ASP.Net application  with full support for Layouts, _ViewStart inclusion, Partials, built-in HtmlHelpers etc.


Introduction

Razor views are a really nice mechanism for generating web page content within an ASP.Net MVC application. They're so great in fact, that it's a shame to use them just to render HTML to display in a browser. What if we could use them to generate other content within our application, like email messages?

Many applications need to send emails. For something simple, like a one-off message to a site administrator, you might be able to get away with using a StringBuilder to generate the content of the email. If you need to send a range of rich HTML emails and want developers to be able to modify them easily, a full-blown template system like Razor is a much more flexible and maintainable mechanism.

Another common requirement is to send emails in an "offline" scenario. By "offline", I mean outside of the context of an HTTP request in a running ASP.Net application. Maybe you want to send emails within a console app, Windows service or an NServiceBus / MassTransit / Rebus message handler.

NonHttpRunTimeRazorSupport lets you use all of Razor's features outside of a running web app.

There are loads of other things you could use this library for - but email is probably one of the most common requirements it supports.

How it Works

The library is on GitHub at https://github.com/danmalcolm/NonHttpRunTimeRazorSupport. If you know about git, go ahead and clone the repo on your development machine, otherwise you can download a zip file of the source or browse the code directly on GitHub.

This solution uses RazorGenerator, a great tool that allows you to pre-compile Razor views at design time. As you edit your Razor views in Visual Studio, a pre-compiled view (an accompanying C# class file a bit like a web forms Something.aspx.Designer.cs file). Pre-compiled views can be used instead of .cshtml files that are compiled on-the-fly by RazorViewEngine. One thing that the RazorGenerator project is missing is an example of how to render pre-compiled Razor views in the "offline" context we introduced above*.

Razor View templates, whether compiled at design-time by RazorGenerator or at runtime by RazorViewEngine, are instances of WebViewPage. Views of this type are designed to execute in the context of an HTTP request. The standard built-in RazorViewEngine assumes a web request and other supporting infrastructure offered by HttpRuntime to be present when locating and rendering Razor views. 

The main purpose of NonHttpRunTimeRazorSupport is to work around this limitation. A customised ViewEngine and fake implementations of HttpContext (and it's HttpRequest, HttpResponse members) are used to support the execution of Razor Views.

Needless to say, you have to write your templates with this "offline" context in mind. Putting something like @HttpContext.Current.Request.ServerVariables[...] in the middle of your template would result in an exception. Note that the library attempts to provide help here by throwing an exception with an informative message if  any non-supported HttpContext (and family) members are accessed.

Getting Started

See the README for details on how to get up and running.

The repository includes a simple EmailDemo console app, that demonstrates how you might handle sending emails in your application.

The EmailNotificationService class uses Razor templates to render email content, using a convention to locate views based on the name of the model type. Here's how the views are structured:


This foundation would allow you to support a range of emails without adding a great deal of code. For each message type, you would simply need to add a model and Razor templates to render the subject, text and html body content. Here's the code for EmailNotificationService in full:

Look at EmailNotificationService.cs on github if you can't see the code in-line above.

Alternatives

There are other libraries that allow you to use Razor templates to generate email content, such as MvcMailer and ActionMailer, but, they don't fully support sending Razor-templated emails in an "offline" scenario ("Email sending from a background process" is still listed as an upcoming feature on the MvcMailer Wiki page and the standalone version of ActionMailer doesn't appear to support layouts and other Razor features.

What Next?

I'll consider adding a NuGet package once I've got feedback from using this approach on some other projects. Any comments or feedback would be great.

I came up with this solution while working on a project that was already using RazorGenerator. I'm interested in exploring whether it would be possible to bypass RazorGenerator and compile the views on the fly. Compiling Razor views from templates stored in a data store could also be interesting....

Happy mailing!

* I should point out that views compiled with the RazorGenerator "Template" generator can run outside of the context of an ASP.Net application. They are not the same thing as standard MVC Razor views - the declarative Layout property, _ViewStart pages, partials, HtmlHelper / UrlHelper etc are not available.


Saturday, 21 September 2013

ControllerPathViewEngine - Structuring View Folders based on Controller Namespace Hierarchy in ASP.Net MVC

Introducing ControllerPathViewEngine

ControllerPathViewEngine is an ASP.Net MVC ViewEngine that allows you to structure your views in folders that match the namespaces of your controller, like this:



If you know about git, go ahead and clone the repository on your development machine, otherwise you can download a zip file of the source. If you just fancy a look around, you can browse the code directly on GitHub.

The Problem

One thing that has always annoyed me about ASP.Net MVC is the flat Views folder structure.

When an ASP.Net MVC controller action renders a view, the built-in view engines will, by default, expect your Views folder to be structured based on the following pattern:

Views/{controller name}/{action name}.{extension}

There's no support for structuring views in folders that relate to the namespaces of their related controllers.

In a simple project with a handful of controllers, this probably won't be a problem. However, on a larger project with a lot of controller code, you'll probably find yourself organising your controllers within a deeper namespace hierarchy.

A nested hierarchy makes a complex application manageable by providing an isolated context to the things it contains, keeping things focussed and easier to name. With the flat view structure we lose some of this context and it becomes difficult to relate view to controller.

And don't even think about having more than one controller with the same name! A flat views folder structure is going to lead to naming clashes, unrelated views sharing the same folder and all sorts of shenannigans.

ASP.Net MVC Areas could alleviate this to some extent by allowing you to group and manage related controllers, views and their related routes in a more modular fashion. They don't really solve the core problem though and using an area only adds a single level to your Views folder structure, based on the following default pattern:

Areas/{area name}/Views/{controller name}/{action name}.{extension}

The Solution

Anyway, that's enough moaning justification - writing the above has taken longer than it took to write the code!

A quick look at the ASP.Net MVC source code led me to an extremely simple solution. We simply intercept the call to FindView and FindPartialView and cheekily switch the folder path for the controller name during the call to the base implementation. This allows us to leverage the core caching and view finding functionality.

The project's README contains some background on implementation and the logic used to drive the view folder structure. See the code itself and unit tests to learn more about the implementation.

Alternatives

I had a quick look around at some other approaches, including this one. I had a couple of problems with this implementation however (in fairness to the writer of the post, the main intention was to demo view engine extensibility). Firstly, the cheesy %1 placeholder used in the view location format strings shows up in the exception thrown when a view cannot be located, which doesn't really help the developer work out which view file is missing. Secondly, I'm not sure that it would support the caching functionality that VirtualPathProviderViewEngine uses to optimise view retrieval when dealing with controllers with the name name (a bit of an edge-case but there's potential for a big WTAF there).

Hope you find this useful. Comments and suggestions are welcome as usual.










Friday, 26 July 2013

Searching all local drives for files using Windows Powershell

There's often a "forensic" side to software development. Unfortunately source control and continuous integration aren't going to be in place on all projects that you work on and, from time to time, you'll need to track down some elusive source code. I remember once helping to track down the source code for an important project on an old hard drive in the company owner's garage!

At the moment, I'm helping to pin down local copies of source code on some developers' PCs and needed a quick way of searching for all Visual Studio solutions on their hard drives. The following Powershell script searches recursively through all folders on all local drives for files with the ".sln" extension and outputs to a file.

Look at the script on github if you can't see the code in-line above.

The main challenge was getting the list of available drives. "Get-PSDrive -PSProvider FileSystem" will get you all local drives, but includes CD / DVD drives which might not be available. Filtering this list further using "Where-Object -Property Free" will restrict the list to available drives.