Posts tagged 'ASP.NET Razor'

08

May 2011

Extending MarkdownHelper/MarkdownDeep.NET to support Google Code's Prettify Syntax Highlighter

I'm in the process of rewriting my blog in ASP.NET MVC to move it from Google App Engine to AppHarbor. One of the important changes is that all my articles will be stored as Markdown instead of HTML.

I've blogged previously about the MarkdownHelper class I created and put on NuGet to make using Markdown in views as trivial as typing:

@Html.Markdown(Model.Body)

One thing that I didn't take care of was adding syntax highlighting to code blocks. On this blog I currently use Google Code's Prettify syntax highlighter, and this requires a CSS class be applied to code blocks:

<pre class="prettyprint">
// Code here
</pre>

By default, MarkdownDeep (which my MarkdownHelper uses) doesn't add these classes, so I had to make some changes to support it. I didn't want to build this into MarkdownHelper as it's fairly specific to using this particular syntax highlighter. Instead, I decided to take advantage of partial classes to allow the functionality to be customised without having to modify the MarkdownHelper.cs file added by MarkdownHelper/NuGet.

First I amended the MarkdownHelper class, adding "partial" to the declaration. This was pushed to NuGet as MarkdownHelper 1.2:

public static partial class MarkdownHelper
{
// ...
}

With this done, I loaded my blog in Visual Studio and opened the Library Package Manager, which showed the update and replaced the class with one click. With this done, I then created a new file named "MarkdownHelper.CodeFormatting.cs" alongside "MarkdownHelper.cs" in my Helpers folder. This is where my customisations should go, which will allow MarkdownHelper.cs to be updated by NuGet without affecting the customisations.

As it turned out, customising the code blocks with MarkdownDeep was pretty easy. The class has several extension points, the interesting one for me being:

public Func<Markdown, string, string> FormatCodeBlock;

If you set this property, when a code block is encountered, your code will be called instead of the default. To make sure I wasn't losing any functionality, I checked the source code of MarkdownDeep to see exactly what it did with a code block:

b.Append("<pre><code>");
foreach (var line in children)
{
    m.HtmlEncodeAndConvertTabsToSpaces(b, line.buf, line.contentStart, line.contentLen);
    b.Append("\n");
}
b.Append("</code></pre>\n\n");

This seemed simple enough, and actually, the code path when you had a custom method was almost the same, it just didn't put the pre/code blocks in:

var sb = new StringBuilder();
foreach (var line in children)
{
    m.HtmlEncodeAndConvertTabsToSpaces(sb, line.buf, line.contentStart, line.contentLen);
    sb.Append("\n");
}
b.Append(m.FormatCodeBlock(m, sb.ToString()));

This meant the only thing I needed to do was surround the string with the pre/code tags. Adding class="prettyprint" was rather trivial:

/// <summary>
/// Overrides the Markdown formatting for code blocks to inject "prettyprint" classes for syntax highlighting.
/// </summary>
private static string FormatCodeBlock(Markdown md, string code)
{
	// Wrap the code in <pre><code> as the default MarkdownDeep.NET implementation does, but add a class of
	// "prettyprint" which is what Google Code Prettify uses to identify code blocks.
	// http://google-code-prettify.googlecode.com/svn/trunk/README.html
	var sb = new StringBuilder();
	sb.Append("<pre class=\"prettyprint\"><code>");
	sb.Append(code);
	sb.Append("</code></pre>\n\n");
	return sb.ToString();
}

Then in my partial class, I simply assigned this method to the FormatCodeBlock property of the Markdown transformer in a static constructor (since this is a partial class, it has direct access to the "markdownTransform" object):

/// <summary>
/// Static constructor to set MarkdownHelper options.
/// </summary>
static MarkdownHelper()
{
    // Override code formatting to support syntax highlighting.
    markdownTransformer.FormatCodeBlock = FormatCodeBlock;
}

With those changes, all my code blocks have the correct class and highlight correctly :-) You can see a sample of a code block on the homepage of the development site where I'm building the blog. You can also see the full source code on Bitbucket (MarkdownHelper.CodeFormatting.cs).

07

May 2011

Setting up NuGet to Automatically Fetch Packages When Deploying to AppHarbor Without Storing Binaries in Source Control

Over the last few days I've been blogging and tweeting about using NuGet without committing the packages folder to source control. David Ebbo blogged about using a pre-build event to fetch packages at build time.

Unfortunately, this doesn't work on AppHarbor because build events are not supported. If you try, you'll find the pre-build step doesn't fire and the build fails due to the missing dependencies :(

I voted on (and tweeted about, lots) a feature request about supporting NuGet natively on AppHarbor. The request has had quite a few votes, and Friism said they'll probably implement it, but in the meantime, this meant I couldn't deploy to AppHarbor. I didn't want to add my packages folder to source control, because even once I remove it when AppHarbor supports NuGet natively, it'll still be in the history (and therefore every clone), which I'd like to avoid.

I scratched my head a little, and re-read the proposed spec for baking this functionality into NuGet/MSBuild (to be honest, I don't actually understand what there is to improve!). The mentions of MSBuild got me thinking - custom build targets!

I opened up my csproj file in Notepad, and scanned through for the "BeforeBuild" step that's included by default but commented out. I removed the comment, and moved my pre-build event into the target:

<Target Name="BeforeBuild">
<Exec Command="&quot;$(SolutionDir)Tools\NuGet&quot; install &quot;$(ProjectDir)packages.config&quot; -o &quot;$(SolutionDir)packages&quot;" />
</Target>

First I checked whether this worked locally, by deleting my packages folder and running MSBuild from the command line. Success! I added in MarkdownHelper and added some test code to a view:

@Html.Markdown("We can use **Markdown!**")

Everything worked fine locally. An "hg ci" and "hg push" later, Bitbucket notified AppHarbor, AppHarbor checked out, built, and successfully deployed!

To show it working, you can see the test page at dev.dantup.com that will form the basis of the ASP.NET MVC / Razor version of this blog, and view the source code that AppHarbor is pulling to see there's no packages folder!

So, it turned out to be quite simple to get it working. I've also notified David Ebbo to see if he'd update his post with this info for anyone reading there that's using AppHarbor! :-)

30

April 2011

Formatting PowerShell Objects using the Razor Engine

I've been meaning to look into PowerShell for a while - it's been on an ever-growing list of things I'd like to learn more about. Recently I got around to spending some time with it and decided an interesting way to learn a little would be to create a module that allowed you to format objects using the Razor Engine.

Edit: When writing this article, I forgot to mention you need to make Powershell run .NET 4 to be able to use Razor. I did this by creating powershell.exe.config and powershell_ise.exe.config files as explained here.

The goal of the module is that you would be able to pipe some objects inand pass some template text, like this:

PS> Get-Command | Format-Razor "Command: @Model.Name, @for (int i = 0; i < 5; i++) { @i }"

Ok, so this sample isn't particularly useful, but you should get the idea. The string passed in could be read from a file, and the output could be written to a file. This will allow you to format objects using Razor, which I think it is a pretty cool templating language.

When I opened PowerShell ISE to start coding, I actually expected this to be only a line or two of code, but it turned out to be a little more complicated. Because Razor compiles to code, and you'd likely only want to do this once regardless of how many times the template would be used, it's not as simple as just passing an object and a template into Razor. First we had to create the Razor Engine:

# Create an instance of the Razor engine for C#
$language = New-Object System.Web.Razor.CSharpRazorCodeLanguage
$host = New-Object System.Web.Razor.RazorEngineHost($language)
    
# Set some default properties for the Razor-generated class
$host.DefaultBaseClass = "TemplateBase" # This is our base class (created below)
$host.DefaultNamespace = "RazorOutput"
$host.DefaultClassName = "Template"

# Add any default namespaces that will be useful to use in the templates
$host.NamespaceImports.Add("System") | Out-Null

New-Object System.Web.Razor.RazorTemplateEngine($host)

This sets up the Razor Engine with some fairly basic settings, including the name and namespace of the generated class, and also the base class. The base class is required to have a few things for Razor to be able to use it:

  • A virtual Execute() method
  • A Write(object) method, called when Razor outputs a variable/expression
  • A WiteLiteral(object) method, called when Razor outputs literal content from the template

Because it's not trivial (or certainly, I couldn't find a way) to create a class inside PowerShell, I cheated a little bit. To avoid including a binary .NET assembly, I compiled the base class from a string on the fly:

# HACK: To avoid shipping a DLL, we're going to just compile our TemplateBase class here
$baseClass = @"
using System.IO;
using System.Text;

	public abstract class TemplateBase
	{
		public StringBuilder Buffer { get; set; }
		public StringWriter Writer { get; set; }
		public dynamic $modelName { get; set; }

		public TemplateBase()
		{
			this.Buffer = new StringBuilder();
			this.Writer = new StringWriter(this.Buffer);
		}

		public abstract void Execute();

		public virtual void Write(object value)
		{
			WriteLiteral(value);
		}

		public virtual void WriteLiteral(object value)
		{
			Buffer.Append(value);
		}
	}
"@
    
# Set up the compiler params, including any references required for the compilation
$codeProvider = New-Object Microsoft.CSharp.CSharpCodeProvider
$assemblies = @(
	[System.Reflection.Assembly]::LoadWithPartialName("System.Core")
		.CodeBase.Replace("file:///", ""),
	[System.Reflection.Assembly]::LoadWithPartialName("Microsoft.CSharp")
		.CodeBase.Replace("file:///", "")
)    
$compilerParams = New-Object System.CodeDom.Compiler.CompilerParameters(,$assemblies)
    
# Compile the template base class    
$templateBaseResults = $codeProvider.CompileAssemblyFromSource($compilerParams, $baseClass);
    
# Add the (just-generated) template base assembly to the compile parameters
$assemblies = $assemblies + $templateBaseResults.CompiledAssembly
	.CodeBase.Replace("file:///", "")
$compilerParams = New-Object System.CodeDom.Compiler.CompilerParameters(,$assemblies)
    
# Compile the Razor-generated code
$codeProvider.CompileAssemblyFromDom($compilerParams, $razorResult.GeneratedCode)

This code first compiles the base class into an assembly, and then calls Razor to generate its own code, which is then compiled with a reference to the first assembly, which contains the base template class.

Finally, we create an instance of the Razor-generated class which we'll finally use for the transformation:

# Grab the assembly that contains the Razor-generated classes
$assembly = $results.CompiledAssembly
    
# Create an instance of our Razor-generated class (this name is hard-coded above)
$type = $assembly.GetType("RazorOutput.Template")
[System.Activator]::CreateInstance($type)

To perform a transformation, all we need to do is set the Model property on the Template, and then call the Execute method:

# Set the model to the current object, using the $modelName param so the user can customise it
$template.$modelName = $model
        
# Execute the code, which writes the output to our buffer
$template.Execute()
        
# "Return" the output for this item
$template.Buffer.ToString()

And that's it, now we can format our objects using a Razor template, as shown earlier:

PS> Get-Command | Format-Razor "Command: @Model.Name, @for (int i = 0; i < 5; i++) { @i }"

The full source code is available here. Feel free to do with it as you wish. If you make any useful changes to it, please do send them over (you can send a Pull Request on Bitbucket if you like). This is my first PowerShell module, so I'm sure there's plenty that can be improved!

27

April 2011

MarkdownHelper on NuGet, using MarkdownDeep

Last month I posted a small HtmlHelper to make transforming Markdown in an ASP.NET MVC application a little easier. Unfortunately, getting it up and running wasn't quite so easy... You had to go and download MarkdownSharp (or copy the code file from the Google Code site) and put it in your project, then copy/paste my code into a file, add the namespace to a Views/Web.config, and blah blah, you gave up already.

Not any more!

I created a package called MarkdownHelper on NuGet. It took me less time to create the package than it took to get up and running previously, but now using the package is as simple as typing the following into the Package Manager Console...

PM> Install-Package MarkdownHelper

... that's it! All done! Now in your views, you can simply type:

@Html.Markdown(Model.YourMarkdownPropertyHere)

This means you no longer have to copy/paste code around. This is mighty useful if you're using Markdown in multiple ASP.NET MVC applications.

Rather than creating another assembly to import, the helper class will be put directly into your project (inside a cryptically named "Helpers" folder), though if this isn't the done thing, I can change it easily enough.

Also worth noting that I changed from MarkdownSharp to MarkdownDeep.NET. In addition to being faster (which isn't really important unless you're transforming a lot of text), it has a Javascript version that works transforms 100% the same, which will come in handy if you're writing an editor. Currently the package imports only the .NET version, though this might change as I add functionality.

14

March 2011

An ASP.NET MVC HtmlHelper Extension Method for Markdown using MarkdownSharp

Update: I changed from using MarkdownSharp to MarkdownDeep.NET and pushed the helper to NuGet to make it easier to use. Sure, it's only a few lines of code, but this way one command will import the MarkdownDeep.NET library and add the namespace to your views Web.config easily. It'll also make updates much simpler if you use it in many places :-)


One of the goals for my new blogging engine in ASP.NET MVC/Razor is to store all of my articles using Markdown instead of HTML. If you don't know what Markdown is, go read. I'll wait... If you've used StackOverflow much, you're probably already familiar with it, even if you didn't know it by name.

Since I first started working on content management systems many moons ago, there was something I disliked about using HTML to store content. Even with strides towards semantic markup, HTML felt a little clunky as a storage format. I wanted something better.

I tried many things to "solve" this problem, including creating my own XML markup for content, which I could transform into HTML for displaying on the website (or plain text, if required). This was totally stupid, and I permit for you to laugh at me (just this once, mind). My XML was 95% the same as HTML. Duh. It made more sense to just store HTML, and convert that into other formats if required, but this still didn't feel right.

As years went by, I really learned to hate rich HTML editors. They usually generate crap markup (why are there 50 divs wrapped around my content?!), and often destroyed relative paths in links and images. They also didn't work on mobile versions of most browsers, meaning I couldn't even fix simple typos when away from my computer. I was sure there had to be a better way, but unfortunately at the time of building this blog, I couldn't find one. As a result, all of the articles currently on this blog are hand-typed HTML in a <textarea>! This has an advantage of working on my iPad, but it kinda sucks. It makes me cry a little inside every time I create a post.

Enter, Markdown

Over the past few years, I've been using StackOverflow quite a lot and I've really grown to love the editor they use, which uses Markdown for formatting. The more I use it, the more I think it's what I should be using for my blog. Not only is the markup much simpler than hand-crafting HTML, it's 100% human-readable in text format and also editable on my iPad!

To test out how this could work, I decided to knock up a quick prototype in ASP.NET MVC/Razor. The code used to transform StackOverflow's content server-side was opensourced under the name MarkdownSharp, so I grabbed that. The project includes a lot of files, but these are mostly tests. The file Markdown.cs is all you need to transform text. I didn't expect it to be difficult, but it still turned out to be far simpler and far more elegant than I was expecting!

Creating an HtmlHelper Extension Method

The best way to use Markdown in an MVC view would be by creating an extension method on the Html property (an instance of HtmlHelper<T> of a view. This will allow us to transform text very easily, like this:

@Html.Markdown(Model.Body)

To do this, we need to create an extension method on HtmlHelper. We do this by using the class we'd like to extend as the first argument, but prefixing it with the keyword "this":

/// <summary>
/// Helper class for transforming Markdown.
/// </summary>
public static class MarkdownHelper
{
	/// <summary>
	/// An instance of the Markdown class that performs the transformations.
	/// </summary>
	static Markdown markdownTransformer = new Markdown();

	/// <summary>
	/// Transforms a string of Markdown into HTML.
	/// </summary>
	/// <param name="helper">HtmlHelper - Not used, but required to make this an extension method.</param>
	/// <param name="text">The Markdown that should be transformed.</param>
	/// <returns>The HTML representation of the supplied Markdown.</returns>
	public static IHtmlString Markdown(this HtmlHelper helper, string text)
	{
		// Transform the supplied text (Markdown) into HTML.
		string html = markdownTransformer.Transform(text);

		// Wrap the html in an MvcHtmlString otherwise it'll be HtmlEncoded and displayed to the user as HTML :(
		return new MvcHtmlString(html);
	}
}

It's important to note that you need to wrap the resulting string in an MvcHtmlString (or other IHtmlString) to stop the framework from HtmlEncoding the output, since in this case the result is deliberately HTML that should be rendered by the browser.

In order to be able to use this method in our views without having to import namespaces, we can add the namespace to the web.config file, like this:

<pages>
	<namespaces>
		<add namespace="DanTup.Web" />
	</namespaces>
</pages>

Now in our view, we'll see our Markdown method in the intellisense once we've typed "Html.", allowing us to pass in the string we'd like to transform.

<div id="content">
	<h2>@Model.Title</h2>
	@Html.Markdown(Model.Body)
</div>

In addition to transforming data in your model, you could also pass a string in directly if you wish to keep your markup a little clearer:

<div id="sidebar">
@Html.Markdown(@"
Links
---

* [Danny Tuppeny](http://blog.dantup.com/)
* [Andrew Nurse](http://vibrantcode.com/)
")
</div>

Unfortunately if you add indenting you'll get strange issues (since indenting means something in Markdown), but for a simple hard-coded list of links/etc. this might be more elegant than ahrd-coding UL/Hyperlinks!