What I learned while updating my church website

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.

Trello

https://trello.com/

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.

AppHarbor

https://appharbor.com/

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

http://twitter.github.com/bootstrap/index.html

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.

sitemap

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.

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.

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

[Test]
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

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.

<div class="row">
    <div class="span9">
        @Html.Raw(Model.ContentHtml)
    </div>
</div>

 

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)

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

routes.IgnoreRoute("{*favicon}", new { favicon = @"(.*/)?favicon.icon(/.*)?" });

 

finding file path of App_Data

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)

@if (someCondition) {
    @: some content not surrounded by a tag element...
}

 

Weird “length==” url with Html.ActionLink

@Html.ActionLink("All", "Index", "Messages", new { Class = "btn btn-primary"})

vs

@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.

@{
    Layout = null;
    Response.ContentType = "text/xml";
}
<?xml version="1.0" encoding="UTF-8"?>
<urlset
      xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:schemaLocation="http://www.sitemaps.org/schemas/sitemap/0.9
      http://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd">

<url>
  <loc>http://www.londonubf.org.uk/</loc>
</url>
...

 

C# Tips

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

message.Title = 
   CultureInfo.CurrentCulture.TextInfo.ToTitleCase(lines[1].ToLower());

Twilio

Nice way to send or receive text message, even phone call.
https://www.twilio.com/

What I learned while updating my church website

Disable browser cache for ajax request on ASP.NET MVC

The application reloads the list of jobs, if any job is reposted or expired. We display different labels, such as “reposted”, “expired”, depending on the action. It worked well with browsers but IE 8.

Simply, IE was cacheing the part of html. Though we request the partial view by post.

update: function () {
    $.ajax({
        type: "GET",
        url: "MJobsB?p=" + list.cPg(),
        contentType: "text/html; charset=utf-8",
        dataType: "html",
        success: function (data) {
            $('#ct #cnt').html(data);
        },
        error: function () { alert("Error Loading Jobs"); }
    });
},

So, we brought in NoCache attribute. And made the action not cached on IE. We made it with attribute, as we want to cache other stuff, especially images.

This is NoCache attribute.

public class NoCacheAttribute : ActionFilterAttribute
{
    public override void OnResultExecuting(ResultExecutingContext filterContext)
    {
        filterContext.HttpContext.Response.Cache.SetExpires(DateTime.UtcNow.AddDays(-1));
        filterContext.HttpContext.Response.Cache.SetValidUntilExpires(false);
        filterContext.HttpContext.Response.Cache.SetRevalidation(HttpCacheRevalidation.AllCaches);
        filterContext.HttpContext.Response.Cache.SetCacheability(HttpCacheability.NoCache);
        filterContext.HttpContext.Response.Cache.SetNoStore();

        base.OnResultExecuting(filterContext);
    }
}

On controller, you just add the attribute.

[NoCache]
[HttpGet]
public ActionResult MJobsB(int cId, int? pg)
{
    return RenderJobListIn("_MJobsB", cId, Mode.List, pg, false);            
}
Disable browser cache for ajax request on ASP.NET MVC

My favourite helpers for ASP.NET MVC

As you use ASP.NET MVC on and on, you start building and using handy html helpers. Rob Conery posted a few of his helpers on his blog. You can find some more in his ASP.NET MVC source code. Inspired by his helpers, here I post mine.

1. Html.Image

Depending on the environment, the absolute url of an image can change. For example, on the dev machine, it is http://localhost:3107/content/images/cancel.gif. On Integration server, if the server host multiple applications under root with the same port numer, 80, the image path can be like http://intserver/app1/content/images/cancel.gif. In this case, the image would not be rendered. SiteRoot is Rob Conery’s helper.

To avoid the situation like the above, I reference image suing SiteRoot functionality.

public static string Image(this HtmlHelper helper, string imageName, string altText)
{
    var builder = new TagBuilder("img");
    builder.MergeAttribute("src",
        UrlHelpers.SiteRoot(helper.ViewContext.HttpContext) + "/content/images/" + imageName);
    builder.MergeAttribute("alt", altText);

    return builder.ToString(TagRenderMode.SelfClosing);
}

2. Html.ClientIdFor

In MVC, you pass viewmodel to View. I often refactor the viewmodel and the change involve property name. When you change a property name of a viewmodel, all server side code like Html.TextBoxFor(m => m.Input.Firstname” change accordingly, but you have to manually change javascript code.

For example,

$('#Input_Firstname').change(resetRate);

You would have to change the id manually. I thought it would be nice to specify clientId with server-side code, and I was not alone. There were guys with the same idea. The following is the code for ClientIdFor and I got the code from John Landheer‘s answer on stackoverflow.

public static MvcHtmlString ClientIdFor<TModel, TProperty>(this HtmlHelper<TModel> helper, Expression<Func<TModel, TProperty>> expression)
{
    return
        MvcHtmlString.Create(
            helper.ViewContext.ViewData.TemplateInfo.GetFullHtmlFieldId(
                ExpressionHelper.GetExpressionText(expression)));
}

3. Script

This writes javascript include statement.

public static string Script(this HtmlHelper helper, string fileName)
{
    if (!fileName.EndsWith(".js"))
        fileName += ".js";

    return string.Format("<script src='{0}/{1}/{2}/{3}' type='text/javascript' ></script>\n",
        helper.SiteRoot(), PUBDIR, SCRIPTDIR, helper.AttributeEncode(fileName));
}

public static string ScriptIe(this HtmlHelper helper, string fileName)
{
    if (!fileName.EndsWith(".js"))
        fileName += ".js";

    return string.Format("<!--[if lt IE 7]>\n<script src='{0}/{1}/{2}/{3}' defer type='text/javascript' ></script>\n<![endif]-->",
        helper.SiteRoot(), PUBDIR, SCRIPTDIR, helper.AttributeEncode(fileName));
}

— To be continued —

My favourite helpers for ASP.NET MVC

To get started with ASP.Net MVC 2

I work on a few personal projects. Personal projects are fun, because you can choose which technology and tools you are going to use and often you can experiment with the latest fancy things. I once used ASP.Net MVC for my toast manager and became a fan of ASP.NET MVC. Also, my experience with ASP.Net MVC helped me greatly with my programming at work, which is Webform based.

ASP.Net MVC 2 has finally come. At the same time, the hard drive in my laptop  went bust, so I replaced it with a new one and installed OS and tools again. Of course, I installed ASP.Net MVC 2 because it is the latest version, and a problem appeared. I couldn’t open my projects in ASP.Net MVC 1!

So, if you worked with ASP.Net MVC 1 and now want to play with MVC 2, you need to prepare yourself a little bit by reading a few articles. Here, they are.

Hope this helps.

To get started with ASP.Net MVC 2

Use Sqlite together wtih ASP.Net MVC

Visual Studio 2008 integrates Sql server 2008 express nicely into App_Data folder. You just create the database using Server Explorer, and it inserts the connection string into web.config for you. I used it when I worked on my toy project – ToastManager. I finished coding and testing on my machine and deployed it to an integration server which belongs to my SCRUM team.

A problem, as usual, arose on the integration box. My windows login had sysadmin access to my local Sql Server express, but the process that runs ToastManager did not on the box.

I blindly thought that Sql Server express is a stand-alone DB and it can be deployed together with the applicaiton. It was misunderstanding. Sql Server Express is still a server application that needs to be installed on a remote machine. I looked for an alternative and soon found that many people use Sqlite.

Sqlite is an open source database and people testify that it is really fast. In order to use with .Net, you need to install System.Data.SQLite, which is another open source ADO.NET provider for SQLite database engine.

Once you download Sqlite, you can use its own command-prompt based tool, but probably, your first reaction is to try to find some GUI management tool. Fortunately, there is one too. It is FireFox Add-on: SQLite Manager.

So, to summarise, you need to have 3 programs to use SQLite. SQLite, System.Data.SQLite, and SQLite Manager.

If you have all necessary programs, please be careful with the folloing points.

  1. Add “SQLite Data Provider” to your web.config. You can add it to your machine.config or web.config in .Net 2’s config folder, but I prefere adding it to project’s web.config.
  2. Do not use “Link To Sql” as it does not support SQLite. Instead, use Entity Framework. It works nicely.
  3. Make sure you have System.Data.SQLite.dll and System.Data.SQLite.Linq.dll in your application’s bin folder. For some reason, though I referenced those two dlls in the project, they were copied into the folder. If those dlls are not in bin folder, you will have “Failed to find or load the registered .Net Framework Data Provider”. (http://sqlite.phxsoftware.com/forums/t/251.aspx)
  4. When you compare data using LINK, do not use .Date property. For some reason, it works with Sql Express, but not with Sqlite. I use “where o.OrderDate >= DateTime.Today” instead of “where o.OrderDate.Date == DateTime.Today”

This is my repository codes I wrote  against SQLite. Hope it helps.

public class OrderRepositorySqlite : IOrderRepository
{
    ToastMasterSqlite _entities = new ToastMasterSqlite(); 
    #region IOrderRepository Members

    public IEnumerable<Order> ListTodayOrders()
    {
        return (from o in _entities.Order
                where o.OrderDate >= DateTime.Today
                select o).ToList();
    }

    public Order GetOrder(long id)
    {
        return (from order in _entities.Order
                where order.OrderId == id
                select order).FirstOrDefault();
    }

    public void Add(Order order)
    {
        _entities.AddToOrder(order);
        _entities.SaveChanges();
    }

    public void Delete(Order order)
    {
        var originalOrder = GetOrder(order.OrderId);
        _entities.DeleteObject(originalOrder);
        _entities.SaveChanges();
    }

    public Order Update(Order orderToUpdate)
    {
        var originalOrder = GetOrder(orderToUpdate.OrderId);
        _entities.ApplyPropertyChanges(originalOrder.EntityKey.EntitySetName, orderToUpdate);
        _entities.SaveChanges();

        return orderToUpdate;
    }

    #endregion
}

Use Sqlite together wtih ASP.Net MVC

Display Selected value with Html.DropDownList in ASP.Net MVC

I wrote ToastManager about two months ago, and not try to covert it into ASP.Net. What ToastManager does is to collect people’s toast order for the morning toast run.

Why? I just think MVC is cool. It separate programming logic into 3 different layers nicely.

The first problem I encountered in writing MVC code was to find a way to display a dropdownlist with a selected value. It is funny. I feel like I am a complete novice when I use MVC.

Simply, you need to pass the selected value in creating SelectList. (from ASP.NET MVC Html.DropDownList SelectedValue)

for example,

    Slices = new SelectList(new int[] {1, 2, 3, 4}, 2);
[

The below is the code for my OrderFormViewModel class


public Models.Order Order { get; private set; }
public string[] Spread { get; private set; }
public SelectList Slices { get; private set; }

public OrderFormViewModel(Order order)
{
    Order = order;
    Spread = new[] { "Butter", "Jam", "Peanut Butter", "Bovril", "Honey", "Marmalade", "Marmite", "Vegemite" };
    int[] sliceList = new int[] {1, 2, 3, 4};
    Slices = new SelectList(sliceList, Order.Slice);
}

Display Selected value with Html.DropDownList in ASP.Net MVC