Using ASP.NET bundling and minification with LESS files

I wanted to use LESS files with the Microsoft ASP.NET Web Optimization Framework. After Googling it with Bing and watching this very nice introduction by Scott Hanselman it appears that most people suggest installing dotless and adding this LessMinify class:

public class LessMinify : CssMinify
{
    public LessMinify()
    {
    }

    public override void Process(BundleContext context, BundleResponse response)
    {
        response.Content = dotless.Core.Less.Parse(response.Content);
        base.Process(context, response);
    }
}

You can then create a bundle like this:

bundles.Add(new DynamicFolderBundle("less", "*.less", new LessMinify()));

This basically says “Whenever I ask you to render styles and the path ends in “less”, get all files that have the .less extension in that path (without the less-part) and apply the LessMinify transformation”. To add the bundle to a (Razor) page, you can do something like this:

@Styles.Render("~/Assets/Styles/less")

This will get you a combination of all “*.less” files in “~/Assets/Styles/” Now, what I find annoying about this is that you can’t specify specific files. With LESS it is possible import one LESS file in another LESS file so you only have to reference the file that contains the imports. With the DynamicFolderBundle approach, you can’t do that unless you put the imported files in another folder.

I have come up with another approach:

public class LessStyleBundle : Bundle
{
    public LessStyleBundle(string virtualPath)
        : base(virtualPath, new LessMinify())
    {
    }

    public LessStyleBundle(string virtualPath, string cdnPath)
        : base(virtualPath, cdnPath, new LessMinify())
    {
    }
}

As you can see I have created a new Bundle. You can use it just like you would use a StyleBundle or a ScriptBundle:

bundles.Add(new LessStyleBundle("~/Assets/Styles/Admin")
    .Include("~/Assets/Styles/admin.less"));

I find this a much nicer and cleaner approach that allows you to use the nice import abilities of LESS without forcing you to use a certain folder structure.

Update (2012/11/20)

I forgot to add something very important. In our LessMinify class, we are passing the less-code as a string to the dotless parser. Because we’re passing it the content and not a file, the dotless parser doesn’t know where to look for the files used in @import statements. To fix this, we can pass a DotlessConfiguration object to the dotless parser. This DotlessConfiguration has a LessSource property which accepts a Type. This Type should be a class that implements the IFileReader interface (in dotless.Core.Input). By creating our own IFileReader implementation, we can tell dotless where to look for the less-files that should be imported.

internal sealed class VirtualFileReader : IFileReader
{
    public byte[] GetBinaryFileContents(string fileName)
    {
        fileName = GetFullPath(fileName);
        return File.ReadAllBytes(fileName);
    }

    public string GetFileContents(string fileName)
    {
        fileName = GetFullPath(fileName);
        return File.ReadAllText(fileName);
    }

    public bool DoesFileExist(string fileName)
    {
        fileName = GetFullPath(fileName);
        return File.Exists(fileName);
    }

    private static string GetFullPath(string path)
    {
        return HostingEnvironment.MapPath("~/Assets/Styles/" + path);
    }
}

With this IFileReader implementation, we can update our LessMinify class:

public class LessMinify : CssMinify
{
    public LessMinify()
    {
    }

    public override void Process(BundleContext context, BundleResponse response)
    {
        var config = new DotlessConfiguration
            {
                MinifyOutput = false,
                ImportAllFilesAsLess = true,
                CacheEnabled = false,
                LessSource = typeof(VirtualFileReader)
            };

        response.Content = dotless.Core.Less.Parse(response.Content, config);
        base.Process(context, response);
    }
}

A consequence of this approach is that all imported less-files have to be in the folder specified in the IFileReader implementation.

Using a FileStreamResult with a MemoryStream in ASP.NET MVC 3

I was playing around with EO.Pdf 2011.2 for .NET from Essential Objects to see how easy it would be to use HTML and Razor to generate a report and convert it to a PDF file.

EO.Pdf allows you to save a PDF document on disk or to a Stream. Since ASP.NET MVC 3 lets you return a FileStreamResult based on a Stream, I thought this would be the easiest solution and I came up with something like this:

[HttpGet]
public FileResult Download()
{
    var doc = new EO.Pdf.PdfDocument();
    EO.Pdf.HtmlToPdf.ConvertUrl("http://www.google.com/", doc);

    var ms = new MemoryStream();    
    doc.Save(ms);
    
    return new FileStreamResult(ms, "application/pdf")
               {
                   FileDownloadName = "download.pdf"
               };
}

When trying it out, all I got was a corrupt PDF file. Something was wrong. But what? Apparently, you have to reset the Position of the MemoryStream before you pass it to the FileStreamResult constructor. Adding this solved the problem.

[HttpGet]
public FileResult Download()
{
    var doc = new EO.Pdf.PdfDocument();
    EO.Pdf.HtmlToPdf.ConvertUrl("http://www.google.com/", doc);

    var ms = new MemoryStream();    
    doc.Save(ms);

    ms.Position = 0;
    
    return new FileStreamResult(ms, "application/pdf")
               {
                   FileDownloadName = "download.pdf"
               };
}

An HtmlHelper to display if a field is required or not in ASP.NET MVC 3

A lot of the users I develop web applications for would like some sort of visual indication for required fields. A commonly used approach for this is putting an asterisk behind the input element.

I could manually put an asterisk behind every required field in my Views, but then I’d have to change my Views whenever the “requiredness” of a field changes. Since I’m using DataAnnotations on my ViewModels to indicate if a field is required or not, it would be nice if I could reuse that information in my Views.

Unfortunately, there is no standard HtmlHelper available to do this. Luckily, it’s rather easy to build one yourself:

using System;
using System.ComponentModel.DataAnnotations;
using System.Linq.Expressions;
using System.Reflection;
using System.Web.Mvc;

public static partial class ExtensionMethods
{
    public static MvcHtmlString RequiredSymbolFor<TModel, TProperty>(
        this HtmlHelper<TModel> htmlHelper,
        Expression<Func<TModel, TProperty>> expression,
        string symbol = "*",
        string cssClass = "editor-field-required")
    {
        ModelMetadata modelMetadata = ModelMetadata.FromLambdaExpression(expression, htmlHelper.ViewData);
        
        if (modelMetadata.IsRequired)
        {
            var builder = new TagBuilder("span");
            builder.AddCssClass(cssClass);
            builder.InnerHtml = symbol;

            return new MvcHtmlString(builder.ToString(TagRenderMode.Normal));
        }
        
        return new MvcHtmlString("");
    }
}

An example

Let’s assume this is our ViewModel. Only the Firstname is required. The Lastname is optional.

public class Person
{
    [Required]
    public string Firstname { get; set; }
    public string Lastname { get; set; }
}

Then you can use it like this in your View:

@using (Html.BeginForm())
{
    <div class="editor-label">
        @Html.LabelFor(model => model.Firstname)
    </div>
    <div class="editor-field">
        @Html.TextBoxFor(model => model.Firstname)
        @Html.RequiredSymbolFor(model => model.Firstname)
        @Html.ValidationMessageFor(model => model.Firstname)
    </div>

    <div class="editor-label">
        @Html.LabelFor(model => model.Lastname)
    </div>
    <div class="editor-field">
        @Html.TextBoxFor(model => model.Lastname)
        @Html.RequiredSymbolFor(model => model.Lastname)
        @Html.ValidationMessageFor(model => model.Lastname)
    </div>
}

This will render something like this:

RequiredSymbolFor HtmlHelper

Hosting Nancy from a console application

A few day ago I needed to add some functionality to an old ASP.NET 1.1 web application in Visual Studio 2003. The version of the .NET framework and Visual Studio are irrelevant to the story, but I just wanted to share my pain. What is relevant, is that the web application needed to perform an HTTP POST request from code-behind to an external website.

After I had added the required code to the old web application, I wanted to test this somehow. Maybe there are easier ways to do this, but I decided I’d quickly create a new blank ASP.NET web site and let the old application send the HTTP POST request to the new dummy site.

That was easier said than done. I don’t know why, but IIS was in a foul mood and refused to cooperate with me that day. I guess he had been flirting with a hot looking Apache server the day before and had tried to get frisky with her but she responded with a 411 error. Anyhow, after wrestling with for over an hour and throwing ASP.NET MVC 3 sites and regular ASP.NET WebForms sites at it, IIS only gave me 404 and 403 errors. At this point I was so mad that I didn’t even want to try Cassini or IIS Express.

Instead, I did what should have done a lot earlier. I just created a new console app, used NuGet to add Nancy.Hosting.Self, added a small NancyModule class and I was finished. I literally had a working solution in less than 3 minutes. And that was the first time I had ever created a self hosted instance of Nancy. I’m starting to understand what they mean with the super-duper-happy-path.

Talk is cheap, show me the codes!

No problem! Just start Visual Studio and create a new Console Application. Open the Package Manager Console and enter Install-Package Nancy.Hosting.Self. This will add Nancy and Nancy.Hosting.Self to your application.

Adjust your main method so it looks like this:

static void Main(string[] args)
{
    var nancyHost = new Nancy.Hosting.Self.NancyHost(new Uri("http://localhost:1234"));
    nancyHost.Start();

    Console.ReadLine();
    nancyHost.Stop();
}

Now add a new class called MainModule.cs (you can actually name it whatever you want) and add this code:

public class MainModule : Nancy.NancyModule
{
    public MainModule()
    {
        Get["/"] = x =>
                       {
                           return "Hello world!";
                       };
    }
}

Now run your application, open a browser and surf to http://localhost:1234. Impressive, no?

Building a photoblog with Nancy and Simple.Data Part 7: The archives

This is the seventh part in my Building a photoblog with Nancy and Simple.Data series:

  1. Setting up the project
  2. Defining the routes
  3. Rendering some views
  4. Adding the database
  5. Updating Simple.Data
  6. Adding comments
  7. The archives

Once again, it’s been a while since the last post. The people behind Nancy and Simple.Data get a lot more work done than I can, so we’ll start this post by updating Nancy and Simple.Data again. Once that is done we tackle the archives pages. With that, we’ll complete the front-end!

Updating Simple.Data

Just like before, open the Package Manager Console and enter Update-Package Simple.Data.SqlCompact40. This will update all three Simple.Data packages to their latest version. At the moment that I’m writing this, that’s version 0.8.2.

Updating Nancy

Remember how we switched to the NuGet versions of the Nancy packages in the previous post? Well, because of that updating Nancy is as easy as updating Simple.Data. Just enter Update-Package Nancy.Hosting.Aspnet and Update-Package Nancy.Viewengine.Razor. This will update all of the Nancy packages to their latest version. At the moment that I’m writing this, that’s version 0.7.1.

There’s one little thing we’ll have to change in our code to ensure that everything works. Open the PhotoBootstrapper class and change the line about the CookieBasedSessions to this:

public class PhotoBootstrapper : DefaultNancyBootstrapper
{
	protected override void InitialiseInternal(TinyIoC.TinyIoCContainer container)
	{
		base.InitialiseInternal(container);
		Nancy.Session.CookieBasedSessions.Enable(
            this,
            Nancy.Cryptography.CryptographyConfiguration.Default);
	}
}

Adding the archives pages

As you might remember from the second post, we have defined three routes for the archives. We’ll deal with them one by one.

The general archives

This page will show a list of all years and months in which photo’s have been published. For example:

Let’s first add a view model for the general archives. Add a class called GeneralArchives to the Models directly and add the following code:

public class GeneralArchives
{
	public List<YearInfo> Years { get; set; }

	public GeneralArchives()
	{
		Years = new List<YearInfo>();
	}
}

public class YearInfo
{
	public int Year { get; set; }
	public int NumberOfPhotos { get; set; }
	public List<MonthInfo> Months { get; set; }

	public YearInfo()
	{
		Months = new List<MonthInfo>();
	}
}

public class MonthInfo
{
	public int Month { get; set; }
	public string MonthName { get; set; }
	public int NumberOfPhotos { get; set; }
}

Now that we have a view model to pass to our view, we can add code to the Get[""] route in the ArchivesModule:

Get[""] = parameters =>
{
	List<DateTime> allDates = DB.Photos
                                .Query()
                                .Select(DB.Photos.DatePublished)
                                .Where(DB.Photos.Published == true)
                                .OrderByDatePublishedDescending()
                                .ToScalarList<DateTime>();

	var model = new GeneralArchives
					{
						Years = (from date in allDates
								 group date by date.Year
								 into yearGroup
								 select new YearInfo
											{
												Year = yearGroup.Key,
												NumberOfPhotos = yearGroup.Count(),
												Months = (from yearDate in yearGroup
														  group yearDate by yearDate.Month
														  into monthGroup
														  select new MonthInfo
																	 {
																		 Month = monthGroup.Key,
																		 MonthName = monthGroup.First().ToString("MMMM"),
																		 NumberOfPhotos = monthGroup.Count()
																	 }).ToList()
											}).ToList()
					};

	return View["general-archives", model];
};

That looks like a lot of code, but there are actually only three things happening. On lines 3 to 8, we ask Simple.Data for a list of all dates on which photo’s have been published. On lines 10 to 29 we use a LINQ operation to transform that list of dates into an instance of our GeneralArchives class filled with nested lists of years and months. Finally, on line 31, we return a view with the GeneralArchives instance as the model.

Speaking of the view, let’s add that one too! Add a file called general-archives.cshtml to the Views directory and add the following code:

<!doctype html>
<html>
<head>
    <meta charset="utf-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">

    <title>Archives</title>
    <link rel="Stylesheet" type="text/css" href="/Content/Css/reset.css" />
    <link rel="Stylesheet" type="text/css" href="/Content/Css/photoblog.css" />

    <script type="text/javascript" src="/Content/Scripts/modernizr-1.7.min.js"></script>
</head>
<body>
    <header><a href="/">MyPhotoBlog</a></header>
    <div id="main">
        <h1>Archives</h1>
        <div id="photo">
            <div>
                <ul class="years">
                @foreach (var yearInfo in Model.Years)
                {
                    <li>
                        <a href="/archives/@yearInfo.Year">@yearInfo.Year</a> (@yearInfo.NumberOfPhotos)
                        <ul class="months">
                            @foreach (var monthInfo in yearInfo.Months)
                            {
                                <li><a href="/archives/@yearInfo.Year/@monthInfo.Month">@monthInfo.MonthName</a> (@monthInfo.NumberOfPhotos)</li>
                            }
                        </ul>
                    </li>
                }
                </ul>
            </div>
        </div>
    </div>
    <footer>MyPhotoBlog is built with <a href="https://github.com/NancyFX/Nancy">Nancy</a> and <a href="https://github.com/markrendle/Simple.Data">Simple.Data</a>.</footer>

    <script type="text/javascript" src="/Content/Scripts/jquery-1.5.2.min.js"></script>
</body>
</html>

On line 20, we start to loop over the YearInfo items in the Years property of our view model. On line 23 we create a link to the yearly archives page for the current YearInfo item and on line 25 we start to loop over the MonthInfo items in the Months property of the current YearInfo item. Finally, on line 27, we also add a link to the monthly archives page for the current MonthInfo item.

The yearly archives

This page will show a list of all photo’s that have been published in the specified year. Building this page is very similar to the general archives. We start with the view model. Add a YearlyArchives class to the Models directory and add this code:

public class YearlyArchives
{
	public List<Photo> Photos { get; set; }
	public int Year { get; set; }

	public YearlyArchives()
	{
		Photos = new List<Photo>();
	}
}

Now we can modify the Get[@"/(?<year>19[0-9]{2}|2[0-9]{3})"] route in the ArchivesModule:

Get[@"/(?<year>19[0-9]{2}|2[0-9]{3})"] = parameters =>
{
	int year = Convert.ToInt32((string)parameters.year);
	DateTime start = new DateTime(year, 1, 1);
	DateTime end = start.AddYears(1);

	List<Photo> photos = DB.Photos
                           .FindAllByDatePublishedAndPublished(start.ToString("yyyy-MM-dd").to(end.ToString("yyyy-MM-dd")), true)
                           .OrderByDatePublished()
                           .ToList<Photo>();

	var model = new YearlyArchives
					{
						Photos = photos,
						Year = year
					};

	return View["yearly-archives", model];
};

As far as I know, it’s not possible to filter records based on a part of the date with Simple.Data. That’s why we just get all records between two dates. To do this, we can use the following Simple.Data notation: FindByDate("2011-01-01".to("2011-12-31")), as you can see on line 8.

Once we have our list of photo’s, we can create an instance of our YearlyArchives view model class and pass it on to a view.

For the view, add a file called yearly-archives.cshtml to the Views directory with the following code:

<!doctype html>
<html>
<head>
    <meta charset="utf-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">

    <title>Archives for @Model.Year</title>
    <link rel="Stylesheet" type="text/css" href="/Content/Css/reset.css" />
    <link rel="Stylesheet" type="text/css" href="/Content/Css/photoblog.css" />

    <script type="text/javascript" src="/Content/Scripts/modernizr-1.7.min.js"></script>
</head>
<body>
    <header><a href="/">MyPhotoBlog</a></header>
    <div id="main">
        <h1>Archives for @Model.Year</h1>
        <div id="photo">
            <div>
                <ul class="photolist">
                @foreach (var photo in Model.Photos)
                {
                    <li><a href="/photo/@photo.Slug">@photo.Name</a> <span class="photodate">(@photo.DatePublished.ToLongDateString())</span></li>
                }
                </ul>
            </div>
        </div>
        <div id="backtoarchives"><a href="/archives">Back to the archives</a></div>
    </div>
    <footer>MyPhotoBlog is built with <a href="https://github.com/NancyFX/Nancy">Nancy</a> and <a href="https://github.com/markrendle/Simple.Data">Simple.Data</a>.</footer>

    <script type="text/javascript" src="/Content/Scripts/jquery-1.5.2.min.js"></script>
</body>
</html>

On lines 7 and 16 we show the correct title for the yearly archive, based on the chosen year. On lines 20 to 23, we loop through the list of photo’s and display a link to the detail page of the photo.

The monthly archives

The monthly archives are very similar to the yearly ones, so I’ll just show the relevant code.

The MonthlyArchives view model class in the Models directory:

public class MonthlyArchives
{
	public string Month { get; set; }
	public List<Photo> Photos { get; set; }

	public MonthlyArchives()
	{
		Photos = new List<Photo>();
	}
}

The Get[@"/(?<year>19[0-9]{2}|2[0-9]{3})/(?<month>[1-9]|1[012])"] route in the ArchivesModule:

Get[@"/(?<year>19[0-9]{2}|2[0-9]{3})/(?<month>[1-9]|1[012])"] = parameters =>
{
	int year = Convert.ToInt32((string)parameters.year);
	int month = Convert.ToInt32((string)parameters.month);
	DateTime start = new DateTime(year, month, 1);
	DateTime end = start.AddMonths(1);

	List<Photo> photos = DB.Photos
                           .FindAllByDatePublishedAndPublished(start.ToString("yyyy-MM-dd").to(end.ToString("yyyy-MM-dd")), true)
                           .OrderByDatePublished()
                           .ToList<Photo>();

	var model = new MonthlyArchives
	{
		Photos = photos,
		Month = start.ToString("MMMM yyyy")
	};

	return View["monthly-archives", model];
};

The monthly-archives.cshtml view in the Views directory:

<!doctype html>
<html>
<head>
    <meta charset="utf-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">

    <title>Archives for @Model.Month</title>
    <link rel="Stylesheet" type="text/css" href="/Content/Css/reset.css" />
    <link rel="Stylesheet" type="text/css" href="/Content/Css/photoblog.css" />

    <script type="text/javascript" src="/Content/Scripts/modernizr-1.7.min.js"></script>
</head>
<body>
    <header><a href="/">MyPhotoBlog</a></header>
    <div id="main">
        <h1>Archives for @Model.Month</h1>
        <div id="photo">
            <div>
                <ul class="photolist">
                @foreach (var photo in Model.Photos)
                {
                    <li><a href="/photo/@photo.Slug">@photo.Name</a> <span class="photodate">(@photo.DatePublished.ToLongDateString())</span></li>
                }
                </ul>
            </div>
        </div>
        <div id="backtoarchives"><a href="/archives">Back to the archives</a></div>
    </div>
    <footer>MyPhotoBlog is built with <a href="https://github.com/NancyFX/Nancy">Nancy</a> and <a href="https://github.com/markrendle/Simple.Data">Simple.Data</a>.</footer>

    <script type="text/javascript" src="/Content/Scripts/jquery-1.5.2.min.js"></script>
</body>
</html>

That’s it!

Whew. Three view models, three routes and three views. I’ve earned myself a drink!

As usual, the code is available on GitHub.

Building a photoblog with Nancy and Simple.Data Part 6: Adding comments

This is the sixth part in my Building a photoblog with Nancy and Simple.Data series:

  1. Setting up the project
  2. Defining the routes
  3. Rendering some views
  4. Adding the database
  5. Updating Simple.Data
  6. Adding comments
  7. The archives

It’s been a while since my last post and a lot of things have changed since then. In this post we’ll update Simple.Data to version 0.6.7 and Nancy to 0.6. Then we’ll see what we need to do to finish the page that displays a photo. Let’s get cracking!

Updating Simple.Data

This is probably the easiest part. Just like before, open the Package Manager Console and enter Update-Package Simple.Data.SqlCompact40. This will update all three Simple.Data packages to their latest version. At the moment that I’m writing this, that’s version 0.6.7.

Updating Nancy

Remember how we added the different Nancy assemblies manually because not all parts were available on Nuget? Well, that’s changed and the release of Nancy 0.6 seems like a good occasion to remove the manually added assemblies and use Nuget to get the latest version of Nancy!

In the Solution Explorer, right click Nancy and choose Remove. Repeat this for Nancy.Hosting.Aspnet and Nancy.ViewEngines.Razor. Delete the three dll-files from the Lib directory in the solution directory as well.

Open the Package Manager Console and enter Install-Package Nancy. When Nancy has been added, use Install-Package Nancy.ViewEngines.Razor and Install-Package Nancy.Hosting.Aspnet to add the rest.

When you rebuild your project, everything should be working just fine. That wasn’t too hard, was it?

Update the PhotoModule

Let’s update the Get["/{slug}"] route so that it displays the requested photo and provides links to the next and previous photo. Because of the amazing work of Mark Rendle on Simple.Data, this is extremely easy:

Get["/{slug}"] = parameters =>
{
	string slug = (string)parameters.slug;
	Models.Photo photo = DB.Photos.FindBySlug(slug);

	if (photo == null)
	{
		// No photo found with this slug, we'll just redirect to the homepage
		return Response.AsRedirect("/");
	}
	else
	{
		var model = new Models.PhotoDetail();
		model.Photo = photo;

		model.PreviousSlug = DB.Photos
		                       .Query()
							   .Select(DB.Photos.Slug)
							   .Where(DB.Photos.Published == true && DB.Photos.DatePublished < photo.DatePublished.Value)
							   .OrderByDatePublishedDescending()
							   .Take(1)
							   .ToScalarOrDefault<string>();

		model.NextSlug = DB.Photos
		                   .Query()
						   .Select(DB.Photos.Slug)
						   .Where(DB.Photos.Published == true && DB.Photos.DatePublished > photo.DatePublished.Value)
						   .OrderByDatePublished()
						   .Take(1)
						   .ToScalarOrDefault<string>();

		return View["photodetail", model];
	}
};

How neat is that? First we select the requested photo, based on the slug. Next we select the slug of previous and next photo’s, put them in the model and let Nancy pass it on to the photodetail.cshtml view. That’s the same view we use for the homepage.

Adding comments

Now that we can browse the photo’s, let’s see how we can give visitors the possibility to add comments.

The Comment class

We already have a table in the database to store the comments. We also need a class that corresponds to that table. Add a new class Comment to the Models directory and give it the following code:

public class Comment
{
	public int Id { get; set; }
	public string Name { get; set; }
	public string Email { get; set; }
	public string Website { get; set; }
	public string Message { get; set; }
	public int PhotoId { get; set; }
	public bool Approved { get; set; }

	public bool IsValid()
	{
		if (String.IsNullOrWhiteSpace(Name)) return false;
		if (String.IsNullOrWhiteSpace(Email))
		{
			return false;
		}
		else
		{
			bool validAddress = true;
			try
			{
				var address = new System.Net.Mail.MailAddress(Email).Address;
			}
			catch (FormatException)
			{
				validAddress = false;
			}

			if (!validAddress) return false;
		}

		if (String.IsNullOrWhiteSpace(Message))
		{
			return false;
		}
		else
		{
			Uri uri;
			if (!System.Uri.TryCreate(Website, UriKind.Absolute, out uri)) return false;
		}
		

		return true;
	}
}

As you can see we have a property for each column in the database table. They have the same name so Simple.Data can map them correctly. I have also added a method called IsValid() to the Comment class. In a larger application you probably want to use a separate validator class, but in this case we’ll just stick with this approach.

Updating our PhotoDetail view model

Since the comments of a photo need to be passed to the view, we need to add some code to the PhotoDetail class:

public class PhotoDetail
{
	public Photo Photo { get; set; }

	public string NextSlug { get; set; }
	public bool HasNext { get { return !String.IsNullOrEmpty(this.NextSlug); } }

	public string PreviousSlug { get; set; }
	public bool HasPrevious { get { return !String.IsNullOrEmpty(this.PreviousSlug); } }

	public string ErrorMessage { get; set; }
	public bool HasErrorMessage { get { return !String.IsNullOrEmpty(this.ErrorMessage); } }

	public List<Comment> Comments { get; set; }

	public PhotoDetail()
	{
		Comments = new List<Comment>();
	}
}

Next to the comments (line 14), I have also added an ErrorMessage property (lines 11 and 12). This will enable us to pass an error message to the view when a user enters incorrect information while adding a comment.

Updating the view

To display the list of comments and a form to add a new comment, I have added this code to the photodetail.cshtml file (right below the </nav>-tag):

<div id="comments">
	<h2>Comments</h2>
	<div id="commentsform">
		<a name="comments"></a>

		@foreach (var comment in @Model.Comments)
		{
			<div class="comment">
				<div class="commenter">
					@if (String.IsNullOrEmpty(comment.Website))
					{
						@comment.Name <text> wrote:</text>
					}
					else
					{
						<a href="@comment.Website">@comment.Name</a> <text> wrote:</text>
					}
				</div>
				<div class="message">@comment.Message</div>
			</div>
		}

		<h3>Add a comment</h3>
		<form action="/photo/@Model.Photo.Slug/addcomment" method="POST">
			@if (@Model.HasErrorMessage)
			{
				<div id="errormessage">
					@Model.ErrorMessage
				</div>
			}
			<div class="formitem">
				<label for="Name">Your name</label>
				<input type="text" id="Name" name="Name" />
				<span class="mandatory">*</span>
			</div>

			<div class="formitem">
				<label for="Email">Your email address</label>
				<input type="text" id="Email" name="Email" />
				<span class="mandatory">*</span>
			</div>

			<div class="formitem">
				<label for="Website">Your website</label>
				<input type="text" id="Website" name="Website" />
			</div>

			<div class="formitem">
				<label for="Message">Your message</label>
				<textarea id="Message" name="Message"></textarea>
				<span class="mandatory">*</span>
			</div>

			<div class="formitem">
				<input type="submit" id="commentsubmit" name="commentsubmit" value="Submit" />
			</div>
		</form>
	</div>
</div>

Notice how the four input elements have the same name as the properties of our Comment class. We’ll see why this is important in a moment. As you can see on line 24, the form will post to /photo/{slug}/addcomment. This route is defined in our PhotoModule. Let’s update it!

Adding a comment

Here’s the new code for the Post["/{slug}/addcomment"] route:

Post["/{slug}/addcomment"] = parameters =>
{
	string photoSlug = (string)parameters.slug;

	int? photoId = DB.Photos
	                 .Query()
					 .Select(DB.Photos.Id)
					 .Where(DB.Photos.Slug == photoSlug)
					 .ToScalarOrDefault<int?>();

	if (photoId.HasValue)
	{
		Models.Comment comment = this.Bind<Models.Comment>("Id", "PhotoId", "Approved");
		comment.PhotoId = photoId.Value;
		comment.Approved = true;

		if (comment.IsValid())
		{
			DB.Comments.Insert(comment);
		}
		else
		{
			Session["commenterror"] = true;
		}

		return Response.AsRedirect(String.Format("/photo/{0}#comments", photoSlug));
	}
	else
	{
		// No photo found with this slug, we'll just redirect to the homepage
		return Response.AsRedirect("/");
	}                
};

On line 5 we’re asking Simple.Data for the Id of the photo the comment should be added to. If no photo is found, we just redirect to the homepage (line 31).

On line 13 we are taking advantage of the ModelBinding capabilities of Nancy. It will try to map the incoming request parameters to the properties of the provided class, Comment in this case. That’s why we gave the input fields in our view the same name as the corresponding properties in the Comment class. The three parameters (Id, PhotoId and Approved) we pass to the Bind<T>() method are blacklisted properties. This means that the Nancy ModelBinder will ignore these properties. We do this to prevent someone from manually posting to our route and proving for example an Id-parameter.

If you’re can’t get this to compile because the Bind<T>() method can’t be found, you’ll probably need to add a using Nancy.ModelBinding; since the Bind<T>() method is actually an extension method for the NancyModule class and it is located in the Nancy.ModelBinding namespace.

Next we check if the entered comment is valid or not. If it is, we ask Simple.Data to add it to the database for us. As you can see this is also very easy to do. You can just pass an object and Simple.Data will figure out what to do with it. If the comment isn’t valid, we set a session variable called commenterror to true. Finally we redirect to the page of the photo to which the comment was or should have been added.

Enabling Session

On line 23 of the Post["/{slug}/addcomment"] route, we set a session variable. If you run the application at this time, that would not work because the session state in Nancy is disabled by default. To enable it, we need to add a bootstrapper to our application. This, like most of the things in Nancy, is easier than it sounds.

Just add a class called PhotoBootstrapper to the root of our project, let it inherit from DefaultNancyBootstrapper and add an override for the InitialiseInternal method:

public class PhotoBootstrapper : DefaultNancyBootstrapper
{
	protected override void InitialiseInternal(TinyIoC.TinyIoCContainer container)
	{
		base.InitialiseInternal(container);
		Nancy.Session.CookieBasedSessions.Enable(this, "ThePassphrase", "SomeSeasoning", "HeresMyHMAC");
	}
}

That’s it. Nancy is smart enough to detect your bootstrapper and use it instead of it’s own default bootstrapper. The four parameters we need to pass to CookieBasedSessions.Enable() are an IApplicationPipelines, a passphrase, a salt and and and hmacPassphrase respectively. You should pick something more random than the values I have provided of course.

Displaying the comments

Now that users are able to add comments, we need to add some code so the comments actually get displayed. This needs to be done in two places: the Get["/{slug}"] route in the PhotoModule class and the Get["/"] route in the RootModule class.

Here is the code for the Get["/{slug}"] route:

Get["/{slug}"] = parameters =>
{
	string slug = (string)parameters.slug;
	Models.Photo photo = DB.Photos.FindBySlug(slug);

	if (photo == null)
	{
		// No photo found with this slug, we'll just redirect to the homepage
		return Response.AsRedirect("/");
	}
	else
	{
		var model = new Models.PhotoDetail();
		model.Photo = photo;

		model.PreviousSlug = DB.Photos
		                       .Query()
							   .Select(DB.Photos.Slug)
							   .Where(DB.Photos.Published == true && DB.Photos.DatePublished < photo.DatePublished.Value)
							   .OrderByDatePublishedDescending()
							   .Take(1)
							   .ToScalarOrDefault<string>();
							   
		model.NextSlug = DB.Photos
		                   .Query()
						   .Select(DB.Photos.Slug)
						   .Where(DB.Photos.Published == true && DB.Photos.DatePublished > photo.DatePublished.Value)
						   .OrderByDatePublished()
						   .Take(1)
						   .ToScalarOrDefault<string>();

		IEnumerable<Models.Comment> comments = DB.Comments
		                                            .FindAll(DB.Comments.PhotoId == model.Photo.Id && DB.Comments.Approved == true)
													.Cast<Models.Comment>();
													
		if (comments != null) model.Comments = comments.ToList();

		bool commenterror = false;
		if (Boolean.TryParse(Convert.ToString(Session["commenterror"]), out commenterror))
		{
			model.ErrorMessage = "Please fill out all required fields and make sure the email address you enter is valid.";
			Session.Delete("commenterror");
		}

		return View["photodetail", model];
	}
};

We ask Simple.Data for a list of comments for the requested photo (lines 32-34) and add it to our view model (line 36). Next we check if the session variable commenterror is set to true or not. If it is, we add an error message to our view model and delete the session variable.

Since the comments also need to be displayed on the homepage, we need to add some code to the Get["/"] route as well:

Get["/"] = parameters =>
{
	var photos = DB.Photos.FindAllByPublished(true).OrderByDatePublishedDescending().Take(2);
	List<Models.Photo> photoList = photos.ToList<Models.Photo>();

	if (photoList.Count > 0)
	{
		var model = new Models.PhotoDetail();
		model.Photo = photoList[0];
		model.NextSlug = String.Empty;

		if (photoList.Count > 1) model.PreviousSlug = photoList[1].Slug;
		else model.PreviousSlug = String.Empty;

		IEnumerable<Models.Comment> comments = DB.Comments
		                                         .FindAll(DB.Comments.PhotoId == model.Photo.Id && DB.Comments.Approved == true)
												 .Cast<Models.Comment>();
												 
		if (comments != null) model.Comments = comments.ToList();

		return View["photodetail", model];
	}
	else
	{
		return View["nophoto"];
	}
};

That’s it!

That’s it for today. Quite a lot of code, but nothing to complicated. As you have seen, Nancy and Simple.Data really make things easy for you!

As usual, the complete code is available on GitHub.

Building a photoblog with Nancy and Simple.Data Part 5: Updating Simple.Data

This is the fifth part in my Building a photoblog with Nancy and Simple.Data series:

  1. Setting up the project
  2. Defining the routes
  3. Rendering some views
  4. Adding the database
  5. Updating Simple.Data
  6. Adding comments
  7. The archives

In the previous post we added a SQL Server Compact 4.0 database to our project and we used Simple.Data to fetch the latest photo to display it on the homepage.

In the previous we were using Simple.Data 0.5.6.0. Since then quite a few things have changed in Simple.Data 0.6.1.0. It is now possible to do paging and sorting at the database level. Let’s see how this impacts our code from the previous post.

Updating Simple.Data

Open the Package Manager Console and enter this command: Update-Package Simple.Data.SqlCompact40. This will fetch the latest version of Simple.Data.SqlCompact40 (and its dependencies Simple.Data.Core and Simple.Data.Ado) from the official NuGet feed and add them to our project. The old versions get removed. That was easy!

Updating our RootModule

Remember how we had to select all published photo’s and manually sort them?

// Get all photo's that are published
var photos = DB.Photos.FindAllByPublished(true);
List<Models.Photo> photoList = photos.ToList<Models.Photo>();

// Order them so the newest come first
photoList = photoList.OrderByDescending(p => p.DatePublished).ToList();

That’s not good for two reasons:

  1. We are requesting an unbound result set. If I happened to be a very active photographer (which I’m not :-) ), that could mean that we are asking the database to return a million records at once. That’s bad.
  2. We have to sort everything in our code. In combination with the unbound result set, this could become very imperformant.

Let’s see how we can solve this with the new features in Simple.Data.

To sort the records directly in the database, we just append .OrderByXXX() or .OrderByXXXDescending() where XXX is the name of column belonging to the table we are querying:

var photos = DB.Photos.FindAllByPublished(true).OrderByDatePublishedDescending();

We now get list of photo’s where Published is set to true and is ordered by the DatePublished field with the highest date first. So now we have an ordered result set, but it’s still an unbound result set. Fixing that is also easy. Since we only need the first two photo’s, we need a top 2. This is solved in a very LINQish way in Simple.Data. We can simply append .Take(2):

var photos = DB.Photos.FindAllByPublished(true).OrderByDatePublishedDescending().Take(2);

That’s all there is to it!

Here is the complete code for the Get["/"] route:

Get["/"] = parameters =>
{
	var photos = DB.Photos.FindAllByPublished(true).OrderByDatePublishedDescending().Take(2);
	List<Models.Photo> photoList = photos.ToList<Models.Photo>();

	if (photoList.Count > 0)
	{
		var model = new Models.PhotoDetail();
		model.Photo = photoList[0];
		model.NextSlug = String.Empty;

		if (photoList.Count > 1) model.PreviousSlug = photoList[1].Slug;
		else model.PreviousSlug = String.Empty;

		return View["photodetail", model];
	}
	else
	{
		return View["nophoto"];
	}
};

That’s it for this time. As usual, the changes to the source code can found on Github.

Building a photoblog with Nancy and Simple.Data Part 4: Adding the database

This is the fourth part in my Building a photoblog with Nancy and Simple.Data series:

  1. Setting up the project
  2. Defining the routes
  3. Rendering some views
  4. Adding the database
  5. Updating Simple.Data
  6. Adding comments
  7. The archives

In the previous post we made a few adjustments to the routes and modules we created in the second post. We added our first view and instructed Nancy to return it when calling a certain route. We also enabled the usage of CSS and Javascript files. We added a Views and Content directory. In this post we will add a SQL Server Compact 4.0 database to the project, setup Simple.Data and make our view a bit more dynamic.

Enabling SQL Server Compact 4.0

Maybe you don’t have the required components to use SQL Server Compact 4.0 databases. To verify this, download the Microsoft Web Platform Installer and run it. Select Products in the top part, select Database in the left and check the list on the right for Microsoft SQL Server Compact 4.0 and Microsoft SQL Server Compact 4.0 Tools. If they are marked as Installed, you’re good to go. If not, click Add next to both of them and finally click Install in the bottom of the application.
Verify that you can use SQL Server Compact 4.0

Adding the database

Right click your project in the Solution Explorer, choose Add, Add ASP.NET Folder and App_Data. This adds the App_Data directory to our project. Right click the App_Data directory and choose Add and New Item…. In the Add New Item dialog, select SQL Server Compact 4.0 Local Database and enter MyPhotoBlog.sdf as the name.
Add the database to the project

Creating the tables

The Server ExplorerDoubleclick the MyPhotoBlog.sdf database in the Solution Explorer. This opens up the Server Explorer you see on the right here. Let’s add a table for our photo’s. To do that, right click the Tables node and choose Create Table. That will open a new window where we can choose a name for the table and add the desired columns.

Enter Photos for the table name and add these columns:

  • Id (int, Primary Key, Identity)
  • Name (nvarchar(100))
  • Slug (nvarchar(100))
  • Filename (nvarchar(100))
  • Camera (nvarchar(100))
  • Lens (nvarchar(100))
  • Aperture (nvarchar(100))
  • Exposure (nvarchar(100))
  • ISO (nvarchar(100))
  • DatePosted (datetime)
  • Published (bit)
  • DatePublished (datetime)

Click OK to save the table and add another one. Call it Comments and add these columns:

  • Id (int, Primary Key, Identity)
  • PhotoId (int, Allow Nulls = No)
  • Name (nvarchar(100))
  • Email (nvarchar(100))
  • Website (nvarchar(100))
  • Message (nvarchar(4000))
  • Approved (bit)

Click OK to save it again.

To add a relation between the two tables, you can right click the Comments table and pick Table Properties. In the Table Properties window, select Add Relations on the left. Enter these details:

  • Relation Name: PhotoComments
  • Update Rule: NO ACTION
  • Delete Rule: CASCADE
  • Primary Key Table: Photos
  • Foreign Key Table: Comments
  • Primary Key Table Column: Id
  • Foreign Key Table Column: PhotoId

Add the relation between Photos and Comments

Click Add Columns and then Add Relation to save the relation.

Adding some test data

To test Simple.Data, we will start with trying to fetch data from the database. Before we can do that, we need to add some data to the database so we will actually have something to fetch.

Right click the MyPhotoBlog.sdf database in the Server Explorer and choose New Query. An Add Table window will popup asking us to add tables. We’ll just ignore this and click Close. We see the query view is divided in four parts. We will only need the third one that shows “SELECT FROM” by default. Replace the “SELECT FROM” with this code:

insert into Photos (Name, Slug, Filename, Camera, Lens, Aperture, Exposure, ISO, DatePosted, Published, DatePublished) values ('First post', 'first-post', 'firstpost.jpg', 'Sony A200', 'Sony 50mm F1.8', 'f/2.0', '1/800', '100', '2011/01/01 12:00:00', 1, '2011/01/01 12:00:00');
insert into Photos (Name, Slug, Filename, Camera, Lens, Aperture, Exposure, ISO, DatePosted, Published, DatePublished) values ('Second post', 'second-post', 'secondpost.jpg', 'Sony A200', 'Sony 50mm F1.8', 'f/4.0', '1/250', '200', '2011/02/01 12:00:00', 1, '2011/02/01 12:00:00');
insert into Photos (Name, Slug, Filename, Camera, Lens, Aperture, Exposure, ISO, DatePosted, Published, DatePublished) values ('Third post', 'third-post', 'thirdpost.jpg', 'Sony A200', 'Tamron 90mm Macro', 'f/8.0', '1/125', '200', '2011/03/01 12:00:00', 1, '2011/03/01 12:00:00');
insert into Photos (Name, Slug, Filename, Camera, Lens, Aperture, Exposure, ISO, DatePosted, Published, DatePublished) values ('Fourth post', 'fourth-post', 'fourthpost.jpg', 'Sony A200', 'Sony 50mm F1.8', 'f/1.8', '1/3200', '100', '2011/04/01 12:00:00', 1, '2011/04/01 12:00:00');
insert into Photos (Name, Slug, Filename, Camera, Lens, Aperture, Exposure, ISO, DatePosted, Published, DatePublished) values ('Fifth post', 'fifth-post', 'fifthpost.jpg', 'Sony A200', 'Sony 50mm F1.8', 'f/16', '8"', '100', '2011/04/09 12:00:00', 1, '2011/04/09 12:00:00');

Now we can press CTRL+R or click the red exclamation mark in the toolbar to execute the query. We get a warning telling us that the query definitions differ and that the query cannot be represented graphically in the Diagram and Criteria Pane. That’s normal. It’s because there is no way to graphically represent insert statements. We’ll Just click Continue. We now get notified that five rows were affected by the last query.

To verify this, right click the MyPhotoBlog.sdf database again and choose Show Table Data. Our five test records are present in the table!

The Data directoryIn addition to some test records in the database, we will also need a photo for each record. We will keep photo’s of our photoblog in a separate directory. Let’s add it to our project. Create a directory called Data in the root of your project and copy the Web.Config file from the Content directory to the new Data directory so Nancy won’t interfere when trying to serve static files from that directory. Now we can put five test photo’s in the Data directory. You can download the samples I use, or you can put some of your own there. Just make sure the names are correct (“firstpost.jpg”, “secondpost.jpg”, …, “fifthpost.jpg”). My photo’s are all 960 pixels wide to fit in the “design” of the photoblog.

Adding Simple.Data to our Modules

The NerdBeers application provided me with some inspiration on how to add Simple.Data to our Modules.

Creating the DBFactory

Add a new directory called Services to your project and add an interface called IDBFactory to it. This is a very simple interface because this is all the code it needs:

public interface IDBFactory
{
	dynamic DB();
}

Just an interface won’t do it. We need something that implements it. Add a new class called DBFactory to the Services directory and add the following code:

public class DBFactory : IDBFactory
{
	protected dynamic _db = null;

	public DBFactory()
	{

	}

	public dynamic DB()
	{
		if (_db == null)
		{
			var c = System.Web.HttpContext.Current;
			var s = ConfigurationManager.ConnectionStrings["MyPhotoBlog"];

			if (s == null || String.IsNullOrWhiteSpace(s.ConnectionString))
			{
				_db = Simple.Data.Database.OpenFile(c.Server.MapPath("~/App_Data/MyPhotoBlog.sdf"));
			}
			else
			{
				_db = Simple.Data.Database.OpenConnection(s.ConnectionString);
			}
		}

		return _db;
	}
}

The DBFactory class has one method: DB(). This method checks if there is a connection string listening to the name “MyPhotoBlog” present in the Web.Config file. If it is, Simple.Data uses that connection string to establish a connection to the database and if it isn’t, we tell Simple.Data to fall back to our MyPhotoBlog.sdf in the App_Data directory.

“Wait a minute,” you say. “What’s this? The DB() method just returns a dynamic. We need something like a DbContext, no?” No we don’t. Simple.Data is built using the awesomeness of dynamics and we’ll get to that in just a minute!

Adding the DBFactory to our Modules

Remember that we created a base class called PhotoblogModule in part 2 of this blog? Well, there was a good reason for that. We’ll use it to take care of calling DBFactory.DB() for all other modules.

Open the PhotoblogModule.cs file (in the Modules directory) and add a dynamic object called DB:

public abstract class PhotoblogModule : NancyModule
{
	protected dynamic DB;

	public PhotoblogModule() : base()
	{

	}

	public PhotoblogModule(string modulePath) : base(modulePath)
	{

	}
}

We now have an object that can store what the DBFactory.DB() will return. We can add this.DB = new DBFactory().DB(); right? Well, yes, we could do that, but there is a better solution. We can make use of something called Dependency Injection. Dependency Injection (or DI) is a form of Inversion of Control (or IoC). What that means is that it inverses the flow of control by injecting dependencies in order to reduce the coupling between components. If you’re not familiar with DI or IoC you’ll probably be a bit confused after reading that. I know I was when I first heard about it.

Let’s say we just use this.DB = new DBFactory().DB(); in the constructor of our PhotoblogModule class. When we do that, we create a tight coupling between the PhotoblogModule class and the DBFactory class. If we ever want to replace the DBFactory class with something else, we would probably have to change our PhotoblogModule as well because they are tightly coupled. Or if we want to Unit Test our PhotoblogModule we would be forced to have a database ready when doing the tests because the PhotoblogModule relies on the DBFactory class.

Dependency Injection solves this for us. In this case we will give the constructors of the PhotoblogModule class a parameter of the type IDBFactory like this:

public abstract class PhotoblogModule : NancyModule
{
	protected dynamic DB;

	public PhotoblogModule(IDBFactory dbFactory) : base()
	{
		DB = dbFactory.DB();
	}

	public PhotoblogModule(IDBFactory dbFactory, string modulePath) : base(modulePath)
	{
		DB = dbFactory.DB();
	}
}

Now, the PhotoblogModule class knows “Ok, I will be provided with something that implements the IDBFactory interface. I don’t know what concrete object I will receive or where it comes from, and frankly, I don’t even care. I only know that whatever is passed to me will at least have a DB() methods which returns a dynamic.”

What is responsible for passing in concrete object implement the IDBFactory interface? In our case, the built in IoC container in Nancy called TinyIoC will take care of everything for us. When creating modules, it will notice that the PhotoblogModule has a dependency on the IDBFactory interface. It will then look for a class implementing that interface and it will inject an instance of that class into the module. Pretty neat, huh?

When we try to build our project, we will get a few errors at this point. That’s because the modules inheriting from our PhotoblogModule will call incorrect constructors of their base class. To fix this, add IDBFactory dbFactory as the first argument of each constructor and pass it to the base() statement like this:

public ArchivesModule(IDBFactory dbFactory) : base(dbFactory, "/archives")
{
    ...
}

Now all our modules have access to a dynamic DB object created by Simple.Data. However, when we build and run the project (CTRL+F5), we get an error! It took me a while to figure this one out, be it appears that Simple.Data.SqlCe40 also needs Simple.Data.Ado to function. Adding it is easy thanks to NuGet. Just enter “Install-Package Simple.Data.Ado” in the Package Manager Console and we’re set!

Reading from the database with Simple.Data

Open the RootModule class. We’ll see how we can get the latest published photo. All we have is the dynamic DB object from our PhotoblogModule base class. Because it is dynamic, we can type DB. followed by whatever we want. Simple.Data will figure out during runtime what to do with whatever you typed.

When we are going to query the database, we will need something to represent the data that is returned. So before we can actually fetch some data, we will need add a Photo class to our project. Create a new directory called Models and in it, create a new class called Photo:

public class Photo
{
	public int Id { get; set; }
	public string Name { get; set; }
	public string Slug { get; set; }
	public string Filename { get; set; }
	public string Camera { get; set; }
	public string Lens { get; set; }
	public string Aperture { get; set; }
	public string Exposure { get; set; }
	public string ISO { get; set; }
	public DateTime DatePosted { get; set; }
	public bool Published { get; set; }
	public DateTime? DatePublished { get; set; }
}

As you can see, this looks very much like what our Photos table in the database looks like.

Add another class to the Models directory and call it PhotoDetail. Add the following code:

public class PhotoDetail
{
	public Photo Photo { get; set; }

	public string NextSlug { get; set; }
	public bool HasNext { get { return !String.IsNullOrEmpty(this.NextSlug); } }

	public string PreviousSlug { get; set; }
	public bool HasPrevious { get { return !String.IsNullOrEmpty(this.PreviousSlug); } }
}

This will serve as the model to pass to our view. It has a way of letting the view know if there is a previous and a next photo and telling it what the slugs are.

Now we can finally try to get something out of our database. On the homepage we need the most recent published photo. That’s the photo where Published is True with the highest DatePublished. Add this code to the Get["/"] route in the RootModule class:

Get["/"] = parameters =>
{
	// Get all photo's that are published
	var photos = DB.Photos.FindAllByPublished(true);
	List<Models.Photo> photoList = photos.ToList<Models.Photo>();

	// Order them so the newest come first
	photoList = photoList.OrderByDescending(p => p.DatePublished).ToList();

	// Get most recent photo
	var latestPhoto = photoList.FirstOrDefault();

	if (latestPhoto != null)
	{
		var model = new Models.PhotoDetail();
		model.Photo = latestPhoto;
		model.NextSlug = String.Empty;

		if (photoList.Count > 1) model.PreviousSlug = photoList[1].Slug;
		else model.PreviousSlug = String.Empty;

		return View["photodetail", model];
	}
	else
	{
		return View["nophoto"];
	}
};

On line 4, we are telling Simple.Data that we want a list of photo’s where the Published field is true. Then we ask for the results as a List<Models.Photo> on line 5.

Of the next part, I’m not really sure if I’m doing this the right way. This is the first time I’m working with Simple.Data and the available documentation is a bit sparse at the moment, but I can’t find a way to pass in an order by clause to the database. That’s why we are doing the sorting of the photo’s on their publication date in the code on line 8. Update: Mark Rendle, the author of Simple.Data just told me that sorting and paging is scheduled for version 0.6.

After the list is sorted, we can get the photo with the highest publication data on line 11. It is of course possible that there is no photo in the database, so we check for that on line 13.

If there is a photo, we put it in an instance of PhotoDetail (line 16). Since this is the latest photo, NextSlug is always empty (line 17). On lines 19 and 20 we check if there is a previous photo. If there is, we set the slug. Finally, we pass our model to the view on line 22.

If no photo was found, we’ll return a view called “nophoto”.

Updating our views

Now that we are sending data back to our view, we need to update our view so it display the incoming data. Change photodetail.cshtml so it looks like this:

<!doctype html>
<html>
<head>
    <meta charset="utf-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">

    <title>My First View</title>
    <link rel="Stylesheet" type="text/css" href="/Content/Css/reset.css" />
    <link rel="Stylesheet" type="text/css" href="/Content/Css/photoblog.css" />

    <script type="text/javascript" src="/Content/Scripts/modernizr-1.7.min.js"></script>
</head>
<body>
    <header>MyPhotoBlog</header>
    <div id="main">
        <div id="name">@Model.Photo.Name</div>
        <div id="photo">
            <img src="/Data/@Model.Photo.Filename" />
        </div>
        <div id="exif">
            <span class="type">Camera:</span> <span id="camera" class="value camera">@Model.Photo.Camera</span>
            <span class="type">Lens:</span> <span id="lens" class="value">@Model.Photo.Lens</span>
            <span class="type">Aperture:</span> <span id="aperture" class="value">@Model.Photo.Aperture</span>
            <span class="type">Exposure:</span> <span id="exposure" class="value">@Model.Photo.Exposure</span>
            <span class="type">ISO:</span> <span id="iso" class="value">@Model.Photo.ISO</span>
        </div>
        <nav>
            @if (Model.HasPrevious)
            {
                <a id="previous" href="/photos/@Model.PreviousSlug">« Previous</a>
            }
            else
            {
                <span id="previous">« Previous</span>
            }

            <a href="/archives" id="archives">Archives</a>

            @if (Model.HasNext)
            {
                <a id="next" href="/photos/@Model.NextSlug">Next »</a>
            }
            else
            {
                <span id="next">Next »</span>
            }
        </nav>
    </div>
    <footer>MyPhotoBlog is built with <a href="https://github.com/NancyFX/Nancy">Nancy</a> and <a href="https://github.com/markrendle/Simple.Data">Simple.Data</a>.</footer>

    <script type="text/javascript" src="/Content/Scripts/jquery-1.5.2.min.js"></script>
</body>
</html>

Testing everything

Ok, this is exciting! Press CTRL+F5 to see if everything works. Isn’t it amazing?
It works!

The “nophoto” view

Since we return a “nophoto” view when there are no photo’s in the database, we need to add that view to our Views directory. Add a new HTM File and call it nophoto.cshtml and add the following code:

<!doctype html>
<html>
<head>
    <meta charset="utf-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">

    <title>Photo's gone missing</title>
    <link rel="Stylesheet" type="text/css" href="/Content/Css/reset.css" />
    <link rel="Stylesheet" type="text/css" href="/Content/Css/photoblog.css" />

    <script type="text/javascript" src="/Content/Scripts/modernizr-1.7.min.js"></script>
</head>
<body>
    <header>MyPhotoBlog</header>
    <div id="main">
        <div id="photo">
            <div>Oops. All our photo's have gone missing. If you find one, please let us know!</div>
        </div>
    </div>
    <footer>MyPhotoBlog is built with <a href="https://github.com/NancyFX/Nancy">Nancy</a> and <a href="https://github.com/markrendle/Simple.Data">Simple.Data</a>.</footer>

    <script type="text/javascript" src="/Content/Scripts/jquery-1.5.2.min.js"></script>
</body>
</html>

Wrapping it up

Our project structureThat’s it for today. We’ve done quite a lot actually. We have installed SQL Server Compact 4.0, added a database to our project, created tables and added test data. Next we have created an IDBFactory interface and a DBFactory class and we have injected it in our modules. Finally, we have made changes to our homepage so that it always show the most recently published photo. On the right you can see the project stucture at this point (click it for a bigger image).

As usual, the changes to the source code can found on Github.

Next time we’ll take care of the rest of the front end views: the PhotoModule and the ArchivesModule.

Building a photoblog with Nancy and Simple.Data Part 3: Rendering some views

This is the third part in my Building a photoblog with Nancy and Simple.Data series:

  1. Setting up the project
  2. Defining the routes
  3. Rendering some views
  4. Adding the database
  5. Updating Simple.Data
  6. Adding comments
  7. The archives

In the previous post we added some NancyModules to our project and defined some routes in them. To test the routes, we let them send a simple string back to the browser. In this post we will first make some small adjustments to our routes and modules based on feedback on the previous post. Next we will see how we can use Razor views to send a response back to the browser. Let’s get started!

Creating an AuthenticationModule

In a comment on the previous post, Steven Robbins (also know as @grumpydev on Twitter) explained that the built in security, which we will use, secures routes at the module level. When we secure the AdminModule class, the login page will also be secured. That means you would have to be logged in to log in. Obviously, we don’t want that :-) That’s why will put the login routes in their own module.

Just like we did in the previous post, we will create a new class in the Modules directory. Let’s call it AuthenticationModule. As you will know by know, the class should inherit from PhotoblogModule. Add these routes:

public class AuthenticationModule : PhotoblogModule
{
	public AuthenticationModule() : base()
	{
		Get["/login"] = parameters =>
		{
			return "Display the login form";
		};

		Post["/login"] = parameters =>
		{
			// Perform validation, then redirect
			return Response.AsRedirect("/admin/photos");
		};

		Post["/logout"] = parameters =>
		{
			// Logout and redirect
			return Response.AsRedirect("/login");
		};
	}
}

Updating the AdminModule

Now that we have put the login routes in the AuthenticationModule, we can remove them from the AdminModule. We also have to add a new route, one which I didn’t think about when writing the previous post. We have added routes to add and edit a photo, but there is no way to delete one. Let’s add that one. After removing the Get[""] and Post["/login"] and adding the Get["/photos/delete/{slug}"] and Post["/photos/delete/{slug}"] routes, our AdminModule looks like this:

public class AdminModule : PhotoblogModule
{
	public AdminModule() : base("/admin")
	{
		Get["/photos"] = parameters =>
		{
			return "A list of all the photo's.";
		};

		Get["/photos/add"] = parameters =>
		{
			return "Display the form to add a photo.";
		};

		Post["/photos/add"] = parameters =>
		{
			// Add the photo, then redirect
			string slug = "newPhoto";
			return Response.AsRedirect("/admin/photos/edit/" + slug);
		};

		Get["/photos/edit/{slug}"] = parameters =>
		{
			return String.Format("Display the form to edit a photo called '{0}'.",
				parameters.slug);
		};

		Post["/photos/edit/{slug}"] = parameters =>
		{
			// Edit the photo, then redirect
			string slug = Convert.ToString(parameters.slug);
			return Response.AsRedirect("/admin/photos/edit/" + slug);
		};

		Get["/photos/delete/{slug}"] = parameters =>
		{
			return String.Format("Are you sure you want to delete the photo called '{0}'?",
				parameters.slug);
		};

		Post["/photos/delete/{slug}"] = parameters =>
		{
			// Delete the photo, then redirect
			return Response.AsRedirect("/admin/photos");
		};

		Get["/comments"] = parameters =>
		{
			return "A list of all the comments.";
		};

		Post["/comments/delete/{id}"] = parameters =>
		{
			// Delete the comment, then redirect
			return Response.AsRedirect("/admin/comments");
		};
	}
}

Improving the ArchivesModule

As TheCodeJunkie (@TheCodeJunkie on Twitter), the main author of Nancy, suggests, we can use a regular expression in the route definitions to filter the archives by year and/or month. I came up with these:

public class ArchivesModule : PhotoblogModule
{
	public ArchivesModule() : base("/archives")
	{
		Get[""] = parameters =>
		{
			return "All photo's of all years and months.";
		};

		Get[@"/(?<year>19[0-9]{2}|2[0-9]{3})"] = parameters =>
		{
			return String.Format("All photo's of the year {0}",
				parameters.year);
		};

		Get[@"/(?<year>19[0-9]{2}|2[0-9]{3})/(?<month>0[1-9]|1[012])"] = parameters =>
		{
			return String.Format("All photo's of month {0} of the year {1}",
				parameters.month,
				parameters.year);
		};
	}
}

This makes sure that the routes only get called when the year parameter is a numerical value between 1900 and 2999. Maybe not ideal, but it does the trick. The month parameter has to be a numerical value between 1 and 12.

That’s it for the changes. On with the show!

Adding our first Razor view

At the moment of writing this post, Nancy expects all views to be located in a Views directory in the root of your application. The view location conventions will get updated in a few weeks, but for now we have to follow the current rules. So, let’s create a new directory called Views in the root of our application. Right click the directory and choose Add and New Item…. Select the HTML Page type, pick photodetail as the name and change the extension to cshtml.

Add the first view

Unfortunately, the Razor ViewEngine implementation in Nancy doesn’t support the use of Layouts (yet?) because the code that enables this isn’t located in the Razor assembly but in the System.Web.Mvc assembly, so each of our Razor files will have contain the complete markup for the page.

Add the following code to the newly created photodetail.cshtml file:

<!doctype html>
<html>
<head>
    <meta charset="utf-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">

    <title>My First View</title>
    <link rel="Stylesheet" type="text/css" href="/Content/Css/reset.css" />
    <link rel="Stylesheet" type="text/css" href="/Content/Css/photoblog.css" />

    <script type="text/javascript" src="/Content/Scripts/modernizr-1.7.min.js"></script>
</head>
<body>
    <header>MyPhotoBlog</header>
    <div id="main">
        <div id="photo">
            <img src="" width="960" height="640" />
        </div>
        <div id="exif">
            <span class="type">Camera:</span> <span id="camera" class="value camera">Sony A200</span>
            <span class="type">Lens:</span> <span id="lens" class="value">Sony DT 50mm F1.8 SAM</span>
            <span class="type">Aperture:</span> <span id="aperture" class="value">f/4</span>
            <span class="type">Exposure:</span> <span id="exposure" class="value">1/250</span>
            <span class="type">ISO:</span> <span id="iso" class="value">200</span>
        </div>
        <nav>
            <a href="#" id="previous">« Previous</a>
            <a href="#" id="archives">Archives</a>
            <a href="#" id="next">Next »</a>
        </nav>
    </div>
    <footer>MyPhotoBlog is built with <a href="https://github.com/thecodejunkie/Nancy">Nancy</a> and <a href="https://github.com/markrendle/Simple.Data">Simple.Data</a>.</footer>

    <script type="text/javascript" src="/Content/Scripts/jquery-1.5.2.min.js"></script>
</body>
</html>

Calling the view from a route

Now that we have created a view, we need to tell Nancy when to call it. Let’s make this view the homepage view. The root route (hehe) is located in our RootModule class. Open that file and adjust the Get["/"] route so that looks like this:

Get["/"] = parameters =>
{
    return View["photodetail"];
};

Using CSS and Javascript

As you might have noticed, the view we just created uses two CSS and two Javascript files. We’ll put all content files (CSS, Javascript and images) in a dedicated directory. Create a directory called Content in the root of your project and add two subdirectories in it: Css and Scripts. I won’t put the full source of the four files in this post, but if you want to follow along, you can download them here.

Testing the view

When you press CTRL+F5, you’ll see that the Nancy now sends our view to the browser. But it looks a bit dull doesn’t it?

What a dull view!

It looks like our CSS isn’t applied. How is that possible? Well. Nancy intercepts all requests. That means that Nancy will also answer requests for CSS or Javascript files and because it doesn’t find a suitable route in of our modules, Nancy returns a 404 Page not found error.

To solve this, we need to hack the Nancy.dll assembly with a hex editor and add some assembly code to override certain low level function calls. Luckily for you, that’s not true :-) All we need to do is give the Content directory its own Web.Config file and disable Nancy for that directory. So add a new Web.Config file to the Content directory (right click the directory, pick Add, New Item… and choose Web Configuration File) and add the following content to it:

<?xml version="1.0"?>
<configuration>
    <system.web>
        <httpHandlers>
            <remove verb="*" path="*"/>
        </httpHandlers>
    </system.web>

    <system.webServer>
        <handlers>
            <remove name="Nancy" />
        </handlers>
    </system.webServer>
</configuration>

Press CTRL+F5 again (or just refresh the browser if it’s still open) and behold! The page still looks a bit dull, but a little less so :-)

A little less dull view!

Wrapping things up

The current project structureIn this post we have made a few adjustments to the routes and modules we created in the previous post. We have added our first view and instructed Nancy to return it when calling a certain route. We’ve also enabled the usage of CSS and Javascript files. We have added a Views and Content directory. Our project structure now looks like the one on the right.

The changes to source code can be found on Github.

In the next post we’ll see how we can replace the static content of the view with something more dynamic by letting Nancy pass in a viewmodel and maybe we’ll finally get started with Simple.Data.

Building a photoblog with Nancy and Simple.Data Part 2: Defining the routes

This is the second part in my Building a photoblog with Nancy and Simple.Data series:

  1. Setting up the project
  2. Defining the routes
  3. Rendering some views
  4. Adding the database
  5. Updating Simple.Data
  6. Adding comments
  7. The archives

In the previous post we created the Visual Studio solution, added the necessary references to Nancy and Simple.Data and tested two basic routes to see if Nancy was setup correctly. In this post we will decide what routes we will need in the application and we will configure our NancyModules accordingly. Let’s get started!

What routes do we need?

To answer that question, we first need to determine what kind of information our application needs to expose and how people should interact with it. Once we know that, we can map the requirements to a route and a method (POST or GET). To make things a bit easier, we will split up the requirements in two parts: front end and back end. The front end of the photoblog is visible for everyone while the back end is only visible for the logged in administrator, me.

Front end requirements

  • Visit the homepage to view the latest photo
      Route: /
      Method: GET

  • Visit a specific photo with its comments
      Route: /photo/{slug}
      Method: GET

  • Add a comment to a specific photo
      Route: /photo/{slug}/addcomment
      Method: POST

  • Visit the general archives
      Route: /archives
      Method: GET

  • Visit the archives for a certain year
      Route: /archives/{year}
      Method: GET

  • Visit the archives for a certain month of a certain year
      Route: /archives/{year}/{month}
      Method: GET

Back end requirement

  • View the login form
      Route: /admin
      Method: GET

  • Login on the system
      Route: /admin/login
      Method: POST

  • View the list of photo’s
      Route: /admin/photos
      Method: GET

  • Add a new photo
      Route: /admin/photos/add
      Method: GET and POST

  • Edit a photo
      Route: /admin/photos/edit/{slug}
      Method: GET and POST

  • View a list of comments
      Route: /admin/comments
      Method: GET

  • Delete a comment
      Route: /admin/comments/delete/{id}
      Method: POST

Now we have that figured out, we can start organizing the routes into modules.

Creating the modules

I think it’s probably easier to split the routes into multiple modules. I think we will need five:

  1. One for the homepage
  2. One for the photo’s
  3. One for the archives
  4. One for the admin stuff
  5. A base class to provide common functionality to the other modules

The base class

For now, the base class doesn’t need to do anything special, but as soon as we start working with the database it will come in handy. Let’s create the base class first so all other modules are future proof.

Create a new directory in the root of the project and call it Modules. Add a new class called PhotoblogModule to that new directory. Make the class abstract, let it inherit from NancyModule and add the following code to the class:

public abstract class PhotoblogModule : NancyModule
{
	public PhotoblogModule() : base()
	{

	}

	public PhotoblogModule(string modulePath) : base(modulePath)
	{

	}
}

This gives us two constructors. We’ve already used the default parameterless constructor in the first post. The second constructor however is new. It accepts one parameter called modulePath and we’ll see what that does in just a moment.

The homepage module

Add another class to the Modules directory and call it RootModule. Let it inherit from the PhotoblogModule class we just made and the one route it should support:

public class RootModule : PhotoblogModule
{
	public RootModule() : base()
	{
		Get["/"] = parameters =>
		{
			return "Homepage";
		};
	}
}

The photo module

Just like before, add a new class to the Modules directory, call it PhotoModule, let it inherit from PhotoblogModule as well and add the two routes it needs to take care of:

using Nancy;

public class PhotoModule : PhotoblogModule
{
	public PhotoModule() : base("/photo")
	{
		Get["/{slug}"] = parameters =>
		{
			return String.Format("Photo '{0}'", parameters.slug);
		};

		Post["/{slug}/addcomment"] = parameters =>
		{
			string photoSlug = Convert.ToString(parameters.slug);
			return Response.AsRedirect("/photo/" + photoSlug);
		};
	}
}

Did you see how we are calling base("/photo")? That’s the second constructor of our PhotoblogModule class. The modulePath parameter makes sure that all routes specified in the Get[] and Post[] indexers are relative to the modulePath. So when we add Get["/{slug}"], we are actually adding Get["/photo/{slug}"].

The archives module

Again, add a new class to the Modules directory, call it ArchivesModule, let it inherit from PhotoblogModule as well and add the routes it needs to handle:

public class ArchivesModule : PhotoblogModule
{
	public ArchivesModule() : base("/archives")
	{
		Get[""] = parameters =>
		{
			return "All photo's of all years and months.";
		};

		Get["/{year}"] = parameters =>
		{
			return String.Format("All photo's of the year {0}",
				parameters.year);
		};

		Get["/{year}/{month}"] = parameters =>
		{
			return String.Format("All photo's of month {0} of the year {1}",
				parameters.month,
				parameters.year);
		};
	}
}

See how we used the second constructor again to specify a modulePath?

The admin module

The last one! Add another class to the Modules directory, call it AdminModule, let it inherit from PhotoblogModule as well and add these routes:

public class AdminModule : PhotoblogModule
{
	public AdminModule() : base("/admin")
	{
		Get[""] = parameters =>
		{
			return "Display the login form.";
		};

		Post["/login"] = parameters =>
		{
			// Perform validation, then redirect
			return Response.AsRedirect("/admin/photos");
		};

		Get["/photos"] = parameters =>
		{
			return "A list of all the photo's.";
		};

		Get["/photos/add"] = parameters =>
		{
			return "Display the form to add a photo.";
		};

		Post["/photos/add"] = parameters =>
		{
			// Add the photo, then redirect
			string slug = "newPhoto";
			return Response.AsRedirect("/admin/photos/edit/" + slug);
		};

		Get["/photos/edit/{slug}"] = parameters =>
		{
			return String.Format("Display the form to edit a photo called '{0}'.",
				parameters.slug);
		};

		Post["/photos/edit/{slug}"] = parameters =>
		{
			// Edit the photo, then redirect
			string slug = Convert.ToString(parameters.slug);
			return Response.AsRedirect("/admin/photos/edit/" + slug);
		};

		Get["/comments"] = parameters =>
		{
			return "A list of all the comments.";
		};

		Post["/comments/delete/{id}"] = parameters =>
		{
			// Delete the comment, then redirect
			return Response.AsRedirect("/admin/comments");
		};
	}
}

There. That was the last of our modules.

Wrapping things up

The project structureNow that we have our five modules in the Modules directory, we can delete the MainModule.cs we created in the first post since we won’t be needing it anymore. All modules and routing information are nicely grouped in the Modules directory. Our project now looks like the one on the right.

The changes to source code can be found on Github.

In the next post we’ll try to replace those temporarily returned strings with Razor views.