What I learned while updating my church website

4 minute read

Working on a person project is lots of fun. You have a whole control over your project, what to do, how to do, and when to do. Also, it gives an opportunity to learn things to a deeper level. Last 3 months, I was learning ruby on rails, following ruby koans and rails book. Honestly, it is not much of a fun just to follow examples on the book, as you just type the code for the sake of practice, not to build anything relevant and meaningful to you.

Anyway, I am happy that I am back to my personal project and actually building something important to me. This post is about what I would learn in the journey - well this sounds like I'm doing something very important, but it's actually my hobby project.



It is a free SCRUM board on the web, developed by the same people who built stackoverflow.com. It is the best online SCRUM board I have ever used.



Mose of people already knows this, I believe. You can deploy your .NET application with git, very easily, and can benefit from their basic free account.

Bootstrap, from twitter


This is more than a CSS framework. It has all the styled layouts, buttons, menus, navigation bars, etc. You can utilise the sleek twitter design on your project. I'm not very good at website design, like most of server side guys, but with the help of this, finally, I can build a decent site for myself and for friends.


Well, I knew this before and used it at work, but this was the first time I implemented sitemap for my personal project.

The definitive guide is sitemmap.org. I also used google webmaster tool to submit the site map.

Regular expression

The Sunday service messages will be saved as files in json format. The file name will have the meta data of the content, for example, title, leacture year and date, chapters, and so on. I use regular expression to parse the file names and retrieve the data.

[sourcecode language="powershell"]
file name: 2012 Genesis 1 1.1-1.25 In The Beginning.json
pattern: ^([0-9]+)\s([0-9A-Za-z]+)\s([0-9]+)\s([0-9.-]+)\s([0-9A-Za-z ]+)

Online regex tester and regex cheatsheet were really helpful to test the pattern.


Names Group

You can use named group to make the pattern more readable.
[sourcecode language="csharp"]
string fileName = "2012 Genesis 1 1.1-1.25 In The Beginning.json";

var pattern = new Regex(@"(?<Year>[0-9]+?)\s(?<Book>[0-9A-Za-z]+?)\s(?<LectureNo>[0-9]+?)\s(?<Chapter>[0-9.-]+?)\s(?<Title>.+?)\.json");
var match = pattern.Match(fileName);

Assert.That(match.Groups["Year"].Value, Is.EqualTo("2012"));
Assert.That(match.Groups["Book"].Value, Is.EqualTo("Genesis"));
Assert.That(match.Groups["LectureNo"].Value, Is.EqualTo("1"));
Assert.That(match.Groups["Chapter"].Value, Is.EqualTo("1.1-1.25"));
Assert.That(match.Groups["Title"].Value, Is.EqualTo("In The Beginning"));


ASP.NET MVC 3 tips

Reading message files from /Content/messages directory

All the messages are in json format and stored in /Content/messages directory. On Get request, the controller read all files in there and render them as list. In my unit test, I used a relative path to the bin directory like

[sourcecode language="csharp"]
public void Should_List_Files_In_The_Message_Directory()
const string messagePath = @"..\..\..\LondonUbf\Content\messages";
var repository = new MessageRepository(messagePath);

var messages = repository.FindAll();

Assert.That(messages.First().FileName.Contains("\\2012 Genesis 1 1.1-1.25 In The Beginning.json"), Is.True);
Assert.That(messages.First().Book, Is.EqualTo("Genesis"));

But web project dll is hosted by iis and the working directory, so I cannot use the relative path. Simply, I used Server.MapPath(). I haven't used it for a while since ASP.NET

[sourcecode language="csharp"]
public ActionResult Messages()
var repository = new MessageRepository(Server.MapPath("/Content/messages"));
var viewModel = new MessageViewModel { Messages = repository.FindAll()};

return View(viewModel);


Output safe HTML string non escaped

Razor view always escapes html, but I need to output the message I read from file system to the page. You can do this with Html.Raw() method.

[sourcecode language="html"]
<div class="row">
<div class="span9">


Custom Error Handling
You can use Application_Error(...) event and log the error and handle it in your way. (http://stackoverflow.com/a/620559/437961)

[sourcecode language="html"]
protected void Application_Error(object sender, EventArgs e)
var exception = Server.GetLastError();

var logger = _container.Resolve<ILogger>();
logger.ErrorFormat("{0}: {1}{2}{3}{4}", DateTime.Now, exception.Message, Environment.NewLine, exception.StackTrace, Environment.NewLine);


/favicon.ico routing issue
If you don't ignore the routing, it will raise an error and fill up the log file soon on every request. Add it to global.asax.cs
[sourcecode language="html"]
routes.IgnoreRoute("{*favicon}", new { favicon = @"(.*/)?favicon.icon(/.*)?" });


finding file path of App_Data
[sourcecode language="csharp"]
string appDataPath = Server.MapPath("~/app_data");


Razor syntax
Using @: to explicitly indicate the start of content

Not all content container blocks start with a tag element tag, though, and there are scenarios where the Razor parser can’t implicitly detect a content block. (from http://weblogs.asp.net/scottgu/archive/2010/12/15/asp-net-mvc-3-razor-s-and-lt-text-gt-syntax.aspx)

[sourcecode language="csharp"]
@if (someCondition) {
@: some content not surrounded by a tag element...


Weird "length==" url with Html.ActionLink

[sourcecode language="csharp"]
@Html.ActionLink("All", "Index", "Messages", new { Class = "btn btn-primary"})


[sourcecode language="csharp"]
@Html.ActionLink("All", "Index", new { controller = "Messages"}, new { Class = "btn btn-primary"})

The first adds length= to the url. It tries to use "Message" as object and convert it to controller name, but string has length property, so it try to use it too. (http://stackoverflow.com/questions/824279/why-does-html-actionlink-render-length-4)

Use the latter and explicitly specify your controller.

Render view in xml

To enhance SEO, I created a sitemap.xml and it needs to be rendered dynamically. By default, the view is rendered as HTML, but you can use classic ASP.NET Response object to change it to xml.

[sourcecode language="csharp"]
Layout = null;
Response.ContentType = "text/xml";
<?xml version="1.0" encoding="UTF-8"?>



C# Tips

Sometimes, you want to capitalise the first letter of each word. This is handy in that case.

[sourcecode language="csharp"]
message.Title =


Nice way to send or receive text message, even phone call.