Custom error pages and error handling in ASP.NET MVC 3

In ASP.NET MVC 3 a new bit of code appeared in the global.asax.cs file:

public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
    filters.Add(new HandleErrorAttribute());
}

The above method is called from the Application_Start() method.

Out of the box, what this does is set up a global filter for handling errors. You can still attribute controller methods or classes as before, but now, if you don’t have a specific HandleErrorAttribute attached to the controller method or class then the global one will take over and be processed.

However, you are not going to get custom errors just yet. If you have a bit of code that causes an exception to be thrown that is not caught then you will just end up with the Yellow Screen of Death as before. For example, this code:

public class HomeController : Controller
{
    // ...

    public ActionResult About()
    {
        throw new Exception("This is not good. Something bad happened.");
    }
}

Will produce this error

Error Handling - Without Custom Error

The missing part of the puzzle is to turn on Custom Errors. This happens in the web.config file. There are three basic options for the mode: “Off” which will show the YSOD to everyone, “RemoteOnly” which shows the YSOD on the local machine (the web server) and the custom error to everyone else, and “On” which shows the custom error to everyone including the local machine.

For development purposes I tend to leave it set to “RemoteOnly” so that I get the YSOD and I get to see what the error is, yet everyone else gets the custom error. However, for developing the actual custom errors themselves we’ll need to set the mode to “On” so we, as developers, get to see the error.

<system.web>
  <customErrors mode="On" defaultRedirect=”~/BadError.htm"/>
</system.web>

The defaultRedirect does not go to a controller action, it is set to a static HTML page that will be displayed if all else goes wrong. This is a final backstop to ensure that the user at least will see something nice event if the error page itself has some issues.

Normally, the error will show the ~/Views/Shared/Error.cshtml view. However, since the view can throw an exception itself there ought to be a backstop custom error page.

The HandleErrorAttribute defaults to using the view "Error" which will display shared view ~/Views/Shared/Error.cshtml.

You can change that by setting the view property on the HandleErrorAttrubute, like this:

public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
    filters.Add(new HandleErrorAttribute {View = "MyCustomError"});
}

I’ve set my error view to display the details of the exception for the purposes of this demo.

@model System.Web.Mvc.HandleErrorInfo

@{
    ViewBag.Title = "Error";
}

<h2>
    Sorry, an error occurred while processing your request.
</h2>
<p>Controller = @Model.ControllerName</p>
<p>Action = @Model.ActionName</p>
<p>Message = @Model.Exception.Message</p>
<p>StackTrace :</p>
<pre>@Model.Exception.StackTrace</pre>

NOTE: In normal production code you would never expose the details of the exception like this. It represents a considerable security risk and a potential attacker could use the information to gain valuable information about your system in order to construct an attack against it.

Now, if we re-run the same application and go to the About page (handled by the HomeController’s About action) then we will get our custom error page.

Performing additional actions on an exception

Overriding OnException in a Controller

If you want to perform additional actions, rather than just simply show a custom error page, then you can override the OnException method from the Controller class on your own controller derived class. If you want to do this for all controllers then  you may want to create a common base controller that all your controllers inherit from. For example:

public class CommonController : Controller
{
    protected override void OnException(ExceptionContext filterContext)
    {
        // Do additional things like logging here.
        base.OnException(filterContext);
    }
}

Then in each of your controllers, inherit from this common controller like this:

public class HomeController : CommonController
{ ...

That will ensure that all your controller have the same functionality.

Creating a FilterAttribute

You could alternatively create a FilterAttribute. This can provide benefits of providing global functionality if you add it to the global filter collection, or very fine grained functionality if you need it on a few sparse controller actions by adding it as an attribute on the controller action.

The filter may look like this:

public class LogExceptionFilterAttribute : FilterAttribute, IExceptionFilter
{
    public void OnException(ExceptionContext filterContext)
    {
        // Log the exception here with your logging framework of choice.
    }
}

If you want to have the filter applied to all controller actions, you can set it up in the RegisterGlobalFilters method in the Global.asax.cs file like this:

public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
    filters.Add(new LogExceptionFilterAttribute());
    filters.Add(new HandleErrorAttribute());
}

Or, if you prefer to have finer grained control you can decorate individual controller classes or controller actions with it, like this:

[LogExceptionFilter()]
public ActionResult About()
{
    throw new Exception("This is not good. Something bad happened.");
}

Tip of the day #22: Obtaining all subdirectories recursively

This is an example of how to obtain a list of all subdirectories using a recursive method with the .NET Framework.

public static List<DirectoryInfo> GetSubdirectories(DirectoryInfo directory)
{
    // Set up the result of the method.List<DirectoryInfo>
    result = new List<DirectoryInfo>();

    // Attempt to get a list of immediate child directories from the directory
    // that was passed in to the method.
    DirectoryInfo[] childDirectories;
    try
    {
        childDirectories = directory.GetDirectories();
    }
    catch (UnauthorizedAccessException uae)
    {
        // If the permissions do not authorise access to the contents of the
        // directory then return an empty list.
        Debug.Print(uae.Message);
        return result;
    }

    // Loop over all the child directories to get their contents.
    foreach (DirectoryInfo childDirectory in childDirectories)
    {
        // Add the child directory to the result list
        result.Add(childDirectory);
        // Get any children of the current child directory
        List<DirectoryInfo> grandchildDirectories = GetSubdirectories(childDirectory);
        // Add the child's children (the grandchildren) to the result list.
        result.AddRange(grandchildDirectories);
    }

    // return the full list of all subdirectories of the one passed in.
    return result;
}

The code requires the following namespaces:

  • System.Collections.Generic

  • System.IO

  • System.Diagnostics

Parallelisation Talk Example – Aggregate Exceptions

The two code examples here show what happens when exceptions are thrown within tasks that are not handled within the task. In each case the task that has the error throws an exception.

In the first example, only one task throws an exception. Although from the output you can see that more tasks were expected to be launched the framework no longer schedules tasks to be started once an exception is thrown. Any existing tasks are continued to completion.

In the second example, all the tasks will throw exceptions. This is just to show that the aggregate exception is bringing back all the exceptions from the various tasks that were running.

Code Example 1 : Some bad

class Program
{
    static void Main(string[] args)
    {
        List<HotelRoomAvailability> hotelList = GetHotels();

        Console.WriteLine("These are the hotels to process");
        foreach(var hotel in hotelList)
            Console.WriteLine(hotel.HotelCode);

        Console.WriteLine(new string('=',79));


        try
        {
            Parallel.ForEach(hotelList, item => PopulateDetails(item));
        }
        catch (AggregateException aggex)
        {
            Console.WriteLine(aggex.Message);
            foreach(Exception ex in aggex.InnerExceptions)
                Console.WriteLine(ex.Message);
        }


        Console.WriteLine("Program finished");
        Console.ReadLine();

    }

    private static void PopulateDetails(HotelRoomAvailability hotel)
    {
        Console.WriteLine("Populating details of {0}", hotel.HotelCode);
        hotel.Name = HotelRespository.GetHotelName(hotel.HotelCode);
        hotel.Rates = AvailabilityRespository.GetRateInformation(
            hotel.HotelCode, hotel.StayDate, hotel.NumberOfNights);
    }

    private static List<HotelRoomAvailability> GetHotels()
    {
        List<HotelRoomAvailability> result = new List<HotelRoomAvailability>
            {
                new HotelRoomAvailability
                    {
                        StayDate = new DateTime(2011, 7, 1),
                        NumberOfNights = 3,
                        HotelCode = "LONSOHO"
                    },
                new HotelRoomAvailability
                    {
                        StayDate = new DateTime(2011, 7, 1),
                        NumberOfNights = 3,
                        HotelCode = "LONLHRT4"
                    },
                new HotelRoomAvailability
                    {
                        StayDate = new DateTime(2011, 7, 1),
                        NumberOfNights = 3,
                        HotelCode = "LONLHRT5" // Not valid
                    },
                new HotelRoomAvailability
                    {
                        StayDate = new DateTime(2011, 7, 1),
                        NumberOfNights = 3,
                        HotelCode = "LONWATERL" // Not valid
                    },
                new HotelRoomAvailability
                    {
                        StayDate = new DateTime(2011, 7, 1),
                        NumberOfNights = 3,
                        HotelCode = "LONLHR123"
                    },
                new HotelRoomAvailability
                    {
                        StayDate = new DateTime(2011, 7, 1),
                        NumberOfNights = 3,
                        HotelCode = "LONCOVGDN"
                    },
                new HotelRoomAvailability
                    {
                        StayDate = new DateTime(2011, 7, 1),
                        NumberOfNights = 3,
                        HotelCode = "LONCTYAIR"
                    },
                new HotelRoomAvailability
                    {
                        StayDate = new DateTime(2011, 7, 1),
                        NumberOfNights = 3,
                        HotelCode = "LONLEISQR"
                    },
                new HotelRoomAvailability
                    {
                        StayDate = new DateTime(2011, 7, 1),
                        NumberOfNights = 3,
                        HotelCode = "LONPADDIN" // Not Valid
                    },
                new HotelRoomAvailability
                    {
                        StayDate = new DateTime(2011, 7, 1),
                        NumberOfNights = 3,
                        HotelCode = "LONHIGHOL"
                    },
                new HotelRoomAvailability
                    {
                        StayDate = new DateTime(2011, 7, 1),
                        NumberOfNights = 3,
                        HotelCode = "LONKINGSX"
                    },
                new HotelRoomAvailability
                    {
                        StayDate = new DateTime(2011, 7, 1),
                        NumberOfNights = 3,
                        HotelCode = "LONEUSTON"
                    }
            };

        return result;
    }
}

Output

The following is output first

These are the hotels to process
LONSOHO
LONLHRT4
LONLHRT5
LONWATERL
LONLHR123
LONCOVGDN
LONCTYAIR
LONLEISQR
LONPADDIN
LONHIGHOL
LONKINGSX
LONEUSTON
========================================================
Populating details of LONSOHO
Populating details of LONWATERL
Populating details of LONCTYAIR
Populating details of LONHIGHOL
Populating details of LONLHRT4

 

Aggregate Exception Example - Before

Then an exception is thrown

Aggregate Exception Example - Exception Assistant

And the final output looks like this:

These are the hotels to process
LONSOHO
LONLHRT4
LONLHRT5
LONWATERL
LONLHR123
LONCOVGDN
LONCTYAIR
LONLEISQR
LONPADDIN
LONHIGHOL
LONKINGSX
LONEUSTON
========================================================
Populating details of LONSOHO
Populating details of LONWATERL
Populating details of LONCTYAIR
Populating details of LONHIGHOL
Populating details of LONLHRT4
One or more errors occurred.
The hotel code 'LONWATERL' does not match a known hotel
Program finished

 

Aggregate Exception Example - After

Code Example 2 : All bad

This example replaces the GetHotels method, above, with a method that creates a list of entirely non-existant hotels:

class Program
{
    static void Main(string[] args)
    {
        List<HotelRoomAvailability> hotelList = GetHotels();

        Console.WriteLine("These are the hotels to process");
        foreach(var hotel in hotelList)
            Console.WriteLine(hotel.HotelCode);

        Console.WriteLine(new string('=',79));


        try
        {
            Parallel.ForEach(hotelList, item => PopulateDetails(item));
        }
        catch (AggregateException aggex)
        {
            Console.WriteLine(aggex.Message);
            foreach(Exception ex in aggex.InnerExceptions)
                Console.WriteLine(ex.Message);
        }


        Console.WriteLine("Program finished");
        Console.ReadLine();

    }

    private static void PopulateDetails(HotelRoomAvailability hotel)
    {
        Console.WriteLine("Populating details of {0}", hotel.HotelCode);
        hotel.Name = HotelRespository.GetHotelName(hotel.HotelCode);
        hotel.Rates = AvailabilityRespository.GetRateInformation(
            hotel.HotelCode, hotel.StayDate, hotel.NumberOfNights);
    }

    private static List<HotelRoomAvailability> GetHotels()
    {
        List<HotelRoomAvailability> result = new List<HotelRoomAvailability>
            {
                new HotelRoomAvailability
                    {
                        StayDate = new DateTime(2011, 7, 1),
                        NumberOfNights = 3,
                        HotelCode = "BRISTOL"
                    },
                new HotelRoomAvailability
                    {
                        StayDate = new DateTime(2011, 7, 1),
                        NumberOfNights = 3,
                        HotelCode = "BIRMINGHAM"
                    },
                new HotelRoomAvailability
                    {
                        StayDate = new DateTime(2011, 7, 1),
                        NumberOfNights = 3,
                        HotelCode = "MANCHESTER" // Not valid
                    },
                new HotelRoomAvailability
                    {
                        StayDate = new DateTime(2011, 7, 1),
                        NumberOfNights = 3,
                        HotelCode = "LIVERPOOL" // Not valid
                    },
                new HotelRoomAvailability
                    {
                        StayDate = new DateTime(2011, 7, 1),
                        NumberOfNights = 3,
                        HotelCode = "CARLISLE"
                    },
                new HotelRoomAvailability
                    {
                        StayDate = new DateTime(2011, 7, 1),
                        NumberOfNights = 3,
                        HotelCode = "CAMBRIDGE"
                    },
                new HotelRoomAvailability
                    {
                        StayDate = new DateTime(2011, 7, 1),
                        NumberOfNights = 3,
                        HotelCode = "OXFORD"
                    },
                new HotelRoomAvailability
                    {
                        StayDate = new DateTime(2011, 7, 1),
                        NumberOfNights = 3,
                        HotelCode = "READING"
                    },
                new HotelRoomAvailability
                    {
                        StayDate = new DateTime(2011, 7, 1),
                        NumberOfNights = 3,
                        HotelCode = "LEEDS" // Not Valid
                    },
                new HotelRoomAvailability
                    {
                        StayDate = new DateTime(2011, 7, 1),
                        NumberOfNights = 3,
                        HotelCode = "NEWCASTLE"
                    },
                new HotelRoomAvailability
                    {
                        StayDate = new DateTime(2011, 7, 1),
                        NumberOfNights = 3,
                        HotelCode = "EDINBURGH"
                    },
                new HotelRoomAvailability
                    {
                        StayDate = new DateTime(2011, 7, 1),
                        NumberOfNights = 3,
                        HotelCode = "GLASGOW"
                    }
            };

        return result;
    }
}

Output

These are the hotels to process
BRISTOL
BIRMINGHAM
MANCHESTER
LIVERPOOL
CARLISLE
CAMBRIDGE
OXFORD
READING
LEEDS
NEWCASTLE
EDINBURGH
GLASGOW
============================================================
Populating details of BRISTOL
Populating details of LIVERPOOL
Populating details of OXFORD
Populating details of NEWCASTLE
Populating details of BIRMINGHAM
One or more errors occurred.
The hotel code 'LIVERPOOL' does not match a known hotel
The hotel code 'OXFORD' does not match a known hotel
The hotel code 'NEWCASTLE' does not match a known hotel
The hotel code 'BIRMINGHAM' does not match a known hotel
The hotel code 'BRISTOL' does not match a known hotel
Program finished

More Information

Parallelisation talk example – Independent Object Graphs

Parallelised code works best when data is not shared. This example shows a simple piece of parallel code where each task operates independently on its own object graph without dependencies on other objects outside its own graph.

Each iteration of the Parallel.ForEach statement operates on only one item in the List named initialObjectGraph. The operations do not make reference to any other objects in the list. The PopulateDetails method (which effectively operates on each item in the list) only uses and updates the information in just that one item.

The program outputs the initial object graph with just the very basic information that is input into it in the GetObjectGraph method. After the Parallel.ForEach has called PopulateDetails on each element in the list, the object graph is output once again

Example Code

class Program
{
    static void Main(string[] args)
    {
        List<HotelRoomAvailability> initialObjectGraph = GetObjectGraph();

        DisplayObjectGraph(initialObjectGraph);

        Parallel.ForEach(initialObjectGraph, item => PopulateDetails(item));

        DisplayObjectGraph(initialObjectGraph);

        Console.WriteLine("Program finished");
        Console.ReadLine();

    }

    private static void PopulateDetails(HotelRoomAvailability hotel)
    {
        Console.WriteLine("Populating details of {0}", hotel.HotelCode);
        hotel.Name = HotelRespository.GetHotelName(hotel.HotelCode);
        hotel.Rates = AvailabilityRespository.GetRateInformation(
            hotel.HotelCode, hotel.StayDate, hotel.NumberOfNights);
    }

    private static List<HotelRoomAvailability> GetObjectGraph()
    {
        List<HotelRoomAvailability> result = new List
            {
                new HotelRoomAvailability
                    {
                        StayDate = new DateTime(2011, 7, 1),
                        NumberOfNights = 3,
                        HotelCode = "LONSOHO"
                    },
                new HotelRoomAvailability
                    {
                        StayDate = new DateTime(2011, 7, 1),
                        NumberOfNights = 3,
                        HotelCode = "LONCOVGDN"
                    },
                new HotelRoomAvailability
                    {
                        StayDate = new DateTime(2011, 7, 1),
                        NumberOfNights = 3,
                        HotelCode = "LONLEISQR"
                    },
                new HotelRoomAvailability
                    {
                        StayDate = new DateTime(2011, 7, 1),
                        NumberOfNights = 3,
                        HotelCode = "LONHIGHOL"
                    },
                new HotelRoomAvailability
                    {
                        StayDate = new DateTime(2011, 7, 1),
                        NumberOfNights = 3,
                        HotelCode = "LONKINGSX"
                    },
                new HotelRoomAvailability
                    {
                        StayDate = new DateTime(2011, 7, 1),
                        NumberOfNights = 3,
                        HotelCode = "LONEUSTON"
                    }
            };

        return result;
    }

    private static void DisplayObjectGraph(
        IEnumerable initialObjectGraph)
    {
        foreach (HotelRoomAvailability availability in initialObjectGraph)
        {
            Console.WriteLine(availability);
            Console.WriteLine(new string('=', 80));
        }
    }
}

Note that not all code is being shown here. There are a number of classes outside the Program class that operate on the data simulated calls to services or other calculations on the data. If you want to see the full example please download the full code sample using the link at the bottom of this post.

Example output

LONSOHO :
Staying on 01/Jul/2011 for 3 nights

================================================================================

LONCOVGDN :
Staying on 01/Jul/2011 for 3 nights

================================================================================

LONLEISQR :
Staying on 01/Jul/2011 for 3 nights

================================================================================

LONHIGHOL :
Staying on 01/Jul/2011 for 3 nights

================================================================================

LONKINGSX :
Staying on 01/Jul/2011 for 3 nights

================================================================================

LONEUSTON :
Staying on 01/Jul/2011 for 3 nights

================================================================================

Populating details of LONSOHO
Populating details of LONCOVGDN
Populating details of LONLEISQR
Populating details of LONHIGHOL
Populating details of LONKINGSX
Populating details of LONEUSTON
LONSOHO : London Soho
Staying on 01/Jul/2011 for 3 nights
Available Rates:
Rate: One Month Advanced Rate Room Only
01/Jul/2011: £48.40
02/Jul/2011: £52.80
03/Jul/2011: £44.00
Total Price: £145.20
----------------------------------------
Rate: One Month Advanced Rate Bed and Breakfast
01/Jul/2011: £58.40
02/Jul/2011: £62.80
03/Jul/2011: £54.00
Total Price: £175.20
----------------------------------------
Rate: One Month Advanced Rate Dinner, Bed and Breakfast
01/Jul/2011: £83.40
02/Jul/2011: £87.80
03/Jul/2011: £79.00
Total Price: £250.20
----------------------------------------
Rate: Two Week Advances Rate Room Only
01/Jul/2011: £54.45
02/Jul/2011: £59.40
03/Jul/2011: £49.50
Total Price: £163.35
----------------------------------------
Rate: Two Week Advances Rate Bed and Breakfast
01/Jul/2011: £64.45
02/Jul/2011: £69.40
03/Jul/2011: £59.50
Total Price: £193.35
----------------------------------------
Rate: Two Week Advances Rate Dinner, Bed and Breakfast
01/Jul/2011: £89.45
02/Jul/2011: £94.40
03/Jul/2011: £84.50
Total Price: £268.35
----------------------------------------
Rate: Fully Flexible Rate Room Only
01/Jul/2011: £60.50
02/Jul/2011: £66.00
03/Jul/2011: £55.00
Total Price: £181.50
----------------------------------------
Rate: Fully Flexible Rate Bed and Breakfast
01/Jul/2011: £70.50
02/Jul/2011: £76.00
03/Jul/2011: £65.00
Total Price: £211.50
----------------------------------------
Rate: Fully Flexible Rate Dinner, Bed and Breakfast
01/Jul/2011: £95.50
02/Jul/2011: £101.00
03/Jul/2011: £90.00
Total Price: £286.50
----------------------------------------

================================================================================

LONCOVGDN : London Covent Garden
Staying on 01/Jul/2011 for 3 nights
Available Rates:
Rate: One Month Advanced Rate Room Only
01/Jul/2011: £59.84
02/Jul/2011: £65.28
03/Jul/2011: £54.40
Total Price: £179.52
----------------------------------------
Rate: One Month Advanced Rate Bed and Breakfast
01/Jul/2011: £69.84
02/Jul/2011: £75.28
03/Jul/2011: £64.40
Total Price: £209.52
----------------------------------------
Rate: One Month Advanced Rate Dinner, Bed and Breakfast
01/Jul/2011: £94.84
02/Jul/2011: £100.28
03/Jul/2011: £89.40
Total Price: £284.52
----------------------------------------
Rate: Two Week Advances Rate Room Only
01/Jul/2011: £67.32
02/Jul/2011: £73.44
03/Jul/2011: £61.20
Total Price: £201.96
----------------------------------------
Rate: Two Week Advances Rate Bed and Breakfast
01/Jul/2011: £77.32
02/Jul/2011: £83.44
03/Jul/2011: £71.20
Total Price: £231.96
----------------------------------------
Rate: Two Week Advances Rate Dinner, Bed and Breakfast
01/Jul/2011: £102.32
02/Jul/2011: £108.44
03/Jul/2011: £96.20
Total Price: £306.96
----------------------------------------
Rate: Fully Flexible Rate Room Only
01/Jul/2011: £74.80
02/Jul/2011: £81.60
03/Jul/2011: £68.00
Total Price: £224.40
----------------------------------------
Rate: Fully Flexible Rate Bed and Breakfast
01/Jul/2011: £84.80
02/Jul/2011: £91.60
03/Jul/2011: £78.00
Total Price: £254.40
----------------------------------------
Rate: Fully Flexible Rate Dinner, Bed and Breakfast
01/Jul/2011: £109.80
02/Jul/2011: £116.60
03/Jul/2011: £103.00
Total Price: £329.40
----------------------------------------

================================================================================

LONLEISQR : London Leicester Square
Staying on 01/Jul/2011 for 3 nights
Available Rates:
Rate: One Month Advanced Rate Room Only
01/Jul/2011: £61.60
02/Jul/2011: £67.20
03/Jul/2011: £56.00
Total Price: £184.80
----------------------------------------
Rate: One Month Advanced Rate Bed and Breakfast
01/Jul/2011: £71.60
02/Jul/2011: £77.20
03/Jul/2011: £66.00
Total Price: £214.80
----------------------------------------
Rate: One Month Advanced Rate Dinner, Bed and Breakfast
01/Jul/2011: £96.60
02/Jul/2011: £102.20
03/Jul/2011: £91.00
Total Price: £289.80
----------------------------------------
Rate: Two Week Advances Rate Room Only
01/Jul/2011: £69.30
02/Jul/2011: £75.60
03/Jul/2011: £63.00
Total Price: £207.90
----------------------------------------
Rate: Two Week Advances Rate Bed and Breakfast
01/Jul/2011: £79.30
02/Jul/2011: £85.60
03/Jul/2011: £73.00
Total Price: £237.90
----------------------------------------
Rate: Two Week Advances Rate Dinner, Bed and Breakfast
01/Jul/2011: £104.30
02/Jul/2011: £110.60
03/Jul/2011: £98.00
Total Price: £312.90
----------------------------------------
Rate: Fully Flexible Rate Room Only
01/Jul/2011: £77.00
02/Jul/2011: £84.00
03/Jul/2011: £70.00
Total Price: £231.00
----------------------------------------
Rate: Fully Flexible Rate Bed and Breakfast
01/Jul/2011: £87.00
02/Jul/2011: £94.00
03/Jul/2011: £80.00
Total Price: £261.00
----------------------------------------
Rate: Fully Flexible Rate Dinner, Bed and Breakfast
01/Jul/2011: £112.00
02/Jul/2011: £119.00
03/Jul/2011: £105.00
Total Price: £336.00
----------------------------------------

================================================================================

LONHIGHOL : London High Holborn
Staying on 01/Jul/2011 for 3 nights
Available Rates:
Rate: One Month Advanced Rate Room Only
01/Jul/2011: £59.84
02/Jul/2011: £65.28
03/Jul/2011: £54.40
Total Price: £179.52
----------------------------------------
Rate: One Month Advanced Rate Bed and Breakfast
01/Jul/2011: £69.84
02/Jul/2011: £75.28
03/Jul/2011: £64.40
Total Price: £209.52
----------------------------------------
Rate: One Month Advanced Rate Dinner, Bed and Breakfast
01/Jul/2011: £94.84
02/Jul/2011: £100.28
03/Jul/2011: £89.40
Total Price: £284.52
----------------------------------------
Rate: Two Week Advances Rate Room Only
01/Jul/2011: £67.32
02/Jul/2011: £73.44
03/Jul/2011: £61.20
Total Price: £201.96
----------------------------------------
Rate: Two Week Advances Rate Bed and Breakfast
01/Jul/2011: £77.32
02/Jul/2011: £83.44
03/Jul/2011: £71.20
Total Price: £231.96
----------------------------------------
Rate: Two Week Advances Rate Dinner, Bed and Breakfast
01/Jul/2011: £102.32
02/Jul/2011: £108.44
03/Jul/2011: £96.20
Total Price: £306.96
----------------------------------------
Rate: Fully Flexible Rate Room Only
01/Jul/2011: £74.80
02/Jul/2011: £81.60
03/Jul/2011: £68.00
Total Price: £224.40
----------------------------------------
Rate: Fully Flexible Rate Bed and Breakfast
01/Jul/2011: £84.80
02/Jul/2011: £91.60
03/Jul/2011: £78.00
Total Price: £254.40
----------------------------------------
Rate: Fully Flexible Rate Dinner, Bed and Breakfast
01/Jul/2011: £109.80
02/Jul/2011: £116.60
03/Jul/2011: £103.00
Total Price: £329.40
----------------------------------------

================================================================================

LONKINGSX : London Kings Cross
Staying on 01/Jul/2011 for 3 nights
Available Rates:
Rate: One Month Advanced Rate Room Only
01/Jul/2011: £61.60
02/Jul/2011: £67.20
03/Jul/2011: £56.00
Total Price: £184.80
----------------------------------------
Rate: One Month Advanced Rate Bed and Breakfast
01/Jul/2011: £71.60
02/Jul/2011: £77.20
03/Jul/2011: £66.00
Total Price: £214.80
----------------------------------------
Rate: One Month Advanced Rate Dinner, Bed and Breakfast
01/Jul/2011: £96.60
02/Jul/2011: £102.20
03/Jul/2011: £91.00
Total Price: £289.80
----------------------------------------
Rate: Two Week Advances Rate Room Only
01/Jul/2011: £69.30
02/Jul/2011: £75.60
03/Jul/2011: £63.00
Total Price: £207.90
----------------------------------------
Rate: Two Week Advances Rate Bed and Breakfast
01/Jul/2011: £79.30
02/Jul/2011: £85.60
03/Jul/2011: £73.00
Total Price: £237.90
----------------------------------------
Rate: Two Week Advances Rate Dinner, Bed and Breakfast
01/Jul/2011: £104.30
02/Jul/2011: £110.60
03/Jul/2011: £98.00
Total Price: £312.90
----------------------------------------
Rate: Fully Flexible Rate Room Only
01/Jul/2011: £77.00
02/Jul/2011: £84.00
03/Jul/2011: £70.00
Total Price: £231.00
----------------------------------------
Rate: Fully Flexible Rate Bed and Breakfast
01/Jul/2011: £87.00
02/Jul/2011: £94.00
03/Jul/2011: £80.00
Total Price: £261.00
----------------------------------------
Rate: Fully Flexible Rate Dinner, Bed and Breakfast
01/Jul/2011: £112.00
02/Jul/2011: £119.00
03/Jul/2011: £105.00
Total Price: £336.00
----------------------------------------

================================================================================

LONEUSTON : London Euston
Staying on 01/Jul/2011 for 3 nights
Available Rates:
Rate: One Month Advanced Rate Room Only
01/Jul/2011: £62.48
02/Jul/2011: £68.16
03/Jul/2011: £56.80
Total Price: £187.44
----------------------------------------
Rate: One Month Advanced Rate Bed and Breakfast
01/Jul/2011: £72.48
02/Jul/2011: £78.16
03/Jul/2011: £66.80
Total Price: £217.44
----------------------------------------
Rate: One Month Advanced Rate Dinner, Bed and Breakfast
01/Jul/2011: £97.48
02/Jul/2011: £103.16
03/Jul/2011: £91.80
Total Price: £292.44
----------------------------------------
Rate: Two Week Advances Rate Room Only
01/Jul/2011: £70.29
02/Jul/2011: £76.68
03/Jul/2011: £63.90
Total Price: £210.87
----------------------------------------
Rate: Two Week Advances Rate Bed and Breakfast
01/Jul/2011: £80.29
02/Jul/2011: £86.68
03/Jul/2011: £73.90
Total Price: £240.87
----------------------------------------
Rate: Two Week Advances Rate Dinner, Bed and Breakfast
01/Jul/2011: £105.29
02/Jul/2011: £111.68
03/Jul/2011: £98.90
Total Price: £315.87
----------------------------------------
Rate: Fully Flexible Rate Room Only
01/Jul/2011: £78.10
02/Jul/2011: £85.20
03/Jul/2011: £71.00
Total Price: £234.30
----------------------------------------
Rate: Fully Flexible Rate Bed and Breakfast
01/Jul/2011: £88.10
02/Jul/2011: £95.20
03/Jul/2011: £81.00
Total Price: £264.30
----------------------------------------
Rate: Fully Flexible Rate Dinner, Bed and Breakfast
01/Jul/2011: £113.10
02/Jul/2011: £120.20
03/Jul/2011: £106.00
Total Price: £339.30
----------------------------------------

================================================================================

Program finished

More information

Prallelisation Talk Example – Tasks within Tasks

In this example I’m showing the launching of further tasks within an existing task.

The Main method launches a single task (of course, it would likely be many tasks in a real system) which is implemented by MainTask and then waits for that task to complete. The MainTask then launches many independent tasks (impelemnted as SubTask and attaches each of them to the parent task (in this case MainTask). This has the effect that when MainTask ends the code in Main is still blocked at the Wait call until all the child tasks have also completed.

Code Example

class Program
{
    static void Main(string[] args)
    {
        Task t = Task.Factory.StartNew(MainTask);
        t.Wait();

        Console.WriteLine("Program finished");
        Console.ReadLine();
    }

    public static void MainTask()
    {
        Console.WriteLine("Starting the Main Task");

        Task.Factory.StartNew(SubTask, TaskCreationOptions.AttachedToParent);
        Task.Factory.StartNew(SubTask, TaskCreationOptions.AttachedToParent);
        Task.Factory.StartNew(SubTask, TaskCreationOptions.AttachedToParent);
        Task.Factory.StartNew(SubTask, TaskCreationOptions.AttachedToParent);
        Task.Factory.StartNew(SubTask, TaskCreationOptions.AttachedToParent);
        Task.Factory.StartNew(SubTask, TaskCreationOptions.AttachedToParent);
        Task.Factory.StartNew(SubTask, TaskCreationOptions.AttachedToParent);
        Task.Factory.StartNew(SubTask, TaskCreationOptions.AttachedToParent);
        Task.Factory.StartNew(SubTask, TaskCreationOptions.AttachedToParent);
        Task.Factory.StartNew(SubTask, TaskCreationOptions.AttachedToParent);

        Console.WriteLine("Ending the Main Task");
    }

    public static void SubTask()
    {
        Console.WriteLine("Starting SubTask {0}", Task.CurrentId);
        Thread.Sleep(2500);
        Console.WriteLine("Ending SubTask {0}", Task.CurrentId);
    }
}

 

Output

Tasks within Tasks Example

Starting the Main Task
Ending the Main Task
Starting SubTask 1
Starting SubTask 2
Starting SubTask 3
Starting SubTask 4
Starting SubTask 5
Starting SubTask 6
Ending SubTask 2
Starting SubTask 7
Ending SubTask 1
Starting SubTask 8
Ending SubTask 3
Starting SubTask 9
Ending SubTask 4
Starting SubTask 10
Ending SubTask 5
Ending SubTask 6
Ending SubTask 7
Ending SubTask 8
Ending SubTask 9
Ending SubTask 10
Program finished

See also

I have also writtent a blog post on Tasks that create more work which may give further insight into this area.

Parallelisation Talk Example – Parallel.Invoke

Parallel.Invoke is the most basic way to start many tasks as the same time. The method takes as many Action<…> based delegates as needed. The Task Parallel Library takes care of the actual scheduling, degree of parallelism etc. Parallel.Invoke itself blocks until all the tasks have completed.

In this example there are two tasks that simply output numbers and letters to the console. One task takes slightly longer than the other. The output shows each task as it runs along with an indication of when it finishes, as well as the overall program finishes.

Code Example One

class Program
{
    static void Main(string[] args)
    {
        // Start two tasks in parallel
        Parallel.Invoke(TaskOne, TaskTwo);

        Console.WriteLine("Finished");
        Console.ReadLine();
    }

    // This task simple outputs the numbers 0 to 9
    private static void TaskOne()
    {
        for (int i = 0; i < 10; i++)
        {
            Console.WriteLine("TaskOne: {0}", i);
            Thread.Sleep(10);
        }
        Console.WriteLine("TaskOne Finished");
    }

    // This task simply outputs the letters A to K
    private static void TaskTwo()
    {
        for(char c = 'A'; c < 'K'; c++)
        {
            Console.WriteLine("TaskTwo: {0}", c);
            Thread.Sleep(5);
        }
        Console.WriteLine("TaskTwo Finished");
    }
}

Example output

Parallel.Invoke Example

TaskOne: 0
TaskTwo: A
TaskOne: 1
TaskTwo: B
TaskTwo: C
TaskTwo: D
TaskOne: 2
TaskTwo: E
TaskOne: 3
TaskTwo: F
TaskTwo: G
TaskTwo: H
TaskOne: 4
TaskTwo: I
TaskTwo: J
TaskOne: 5
TaskTwo Finished
TaskOne: 6
TaskOne: 7
TaskOne: 8
TaskOne: 9
TaskOne Finished
Finished

Code Example: Variation using Task.Factory.StartNew

The Parallel.Invoke method is equivalent setting up a number of tasks using Task.Factory.StartNew(…) then Task.WaitAll(…).

The following code example shows the same code (Main method only) but using Task.Factory.StartNew:

static void Main(string[] args)
{
    // Start two tasks in parallel
    Task t1 = Task.Factory.StartNew(TaskOne);
    Task t2 = Task.Factory.StartNew(TaskTwo);
    Task.WaitAll(t1, t2);

    Console.WriteLine("Finished");
    Console.ReadLine();
}

Parallelisation Talk Example – ConcurrentBag

This example shows a ConcurrentBag being populated and it being accessed while another task is still populating the bag.

The ConcurrentBag class can be found in the System.Collections.Concurrent namespace

In this example, the ConcurrentBag is populated in task that is running in the background. After a brief pause in order to allow the background task time to put some items in the bag, main thread starts outputting the contents of the bag.

When the code starts to iterate over the bag, a snapshot is taken so that the enumeration is not tripped up by additional items being added or removed from the bag elsewhere. You can see this effect as only 13 items are output, yet immediately afterwards the bag has 20 items (in this example, if you run the code yourself you may get different results)

Code Example

class Program
{
    private static ConcurrentBag<string> bag = new ConcurrentBag<string>();
    static void Main(string[] args)
    {
        // Start a task to run in the background.
        Task.Factory.StartNew(PopulateBag);

        // Wait a wee bit so that the bag can get populated
        // with some items before we attempt to output them.
        Thread.Sleep(25);

        // Display the contents of the bag
        int count = 0;
        foreach (string item in bag)
        {
            count++;
            Console.WriteLine(item);
        }

        // Show the difference between the count of items
        // displayed and the current state of the bag
        Console.WriteLine("{0} items were output", count);
        Console.WriteLine("The bag contains {0} items", bag.Count);

        Console.ReadLine();
    }

    public static void PopulateBag()
    {
        for (int i = 0; i < 200; i++ )
        {
            bag.Add(string.Format("This is item {0}", i));

            // Wait a bit to simulate other processing.
            Thread.Sleep(1);
        }

        // Show the final size of the bag.
        Console.WriteLine("Finished populating the bag with {0} items.", bag.Count);
    }
}

Typical output

ConcurrentBag Example

This is item 12
This is item 11
This is item 10
This is item 9
This is item 8
This is item 7
This is item 6
This is item 5
This is item 4
This is item 3
This is item 2
This is item 1
This is item 0
13 items were output
The bag contains 20 items
Finished populating the bag with 200 items.

Parallelisation Talk Examples – ConcurrentDictionary

The example used in the talk was one I had already blogged about. The original blog entry the example was based upon is here: Parallelisation in .NET 4.0 – The ConcurrentDictionary.

Code Example

class Program
{
    private static ConcurrentDictionary<string, int> wordCounts =
        new ConcurrentDictionary<string, int>();

    static void Main(string[] args)
    {
        string[] lines = File.ReadAllLines("grimms-fairy-tales.txt");
        Parallel.ForEach(lines, ProcessLine);

        Console.WriteLine("There are {0} distinct words", wordCounts.Count);
        var topForty = wordCounts.OrderByDescending(kvp => kvp.Value).Take(40);
        foreach (KeyValuePair word in topForty)
        {
            Console.WriteLine("{0}: {1}", word.Key, word.Value);
        }
        Console.ReadLine();
    }

    private static void ProcessLine(string line)
    {
        var words = line.Split(' ')
            .Select(w => w.Trim().ToLowerInvariant())
            .Where(w => !string.IsNullOrEmpty(w));
        foreach (string word in words)
            CountWord(word);
    }

    private static void CountWord(string word)
    {
        if (!wordCounts.TryAdd(word, 1))
            UpdateCount(word);
    }

    private static void UpdateCount(string word)
    {
        int value = wordCounts[word];
        if (!wordCounts.TryUpdate(word, value + 1, value))
        {
            Console.WriteLine("Failed to count '{0}' (was {1}), trying again...",
                word, value);

            UpdateCount(word);
        }
    }
}

Downloads

Parallelisation Talk Examples – Basic PLINQ

These are some code examples from my introductory talk on Parallelisation showing the difference between a standard sequential LINQ query and its parallel equivalent.

The main differences between this and the previous two examples (Parallel.For and Parallel.ForEach) is that LINQ (and PLINQ) is designed to return data back, so the LINQ expression uses a Func<TResult, T1, T2, T3…> instead of an Action<T1, T2, T3…>. Since the examples were simply outputting a string to the Console to indicate which item or index was being processed I’ve changed the code to return a string back to the LINQ expression. The results are then looped over and output to the console.

It is also important to remember that LINQ expressions are not evaluated until the data is called for. In the example below that is with the .ToList() method call, however it may also be as a result of foreach or any other method of iterating over the expression results.

Code example 1: Sequential processing of data with LINQ

class Program
{
    private static Random rnd = new Random();

    static void Main(string[] args)
    {
        DateTime start = DateTime.UtcNow;

        IEnumerable<int> items = Enumerable.Range(0, 20);

        var results = items
            .Select(ProcessItem)
            .ToList();

        results.ForEach(Console.WriteLine);

        DateTime end = DateTime.UtcNow;
        TimeSpan duration = end - start;

        Console.WriteLine("Finished. Took {0}", duration);

        Console.ReadLine();
    }

    private static string ProcessItem(int item)
    {
        // Simulate similar but slightly variable length processing
        int pause = rnd.Next(900, 1100);
        Thread.Sleep(pause);

        return string.Format("Result of item {0}", item);
    }
}

The output of the above code may look something like this:

Basic LINQ

As you can see this takes roughly of 20 seconds to process 20 items with each item taking about one second to process.

Code Example 2: Parallel processing of data with PLINQ

The AsParallel extension method can be found in the System.Linq namespace so no additional using statements are needed if you are already using LINQ.

class Program
{
    private static Random rnd = new Random();

    static void Main(string[] args)
    {
        DateTime start = DateTime.UtcNow;

        IEnumerable<int> items = Enumerable.Range(0, 20);

        var results = items.AsParallel()
            .Select(ProcessItem)
            .ToList();

        results.ForEach(Console.WriteLine);

        DateTime end = DateTime.UtcNow;
        TimeSpan duration = end - start;

        Console.WriteLine("Finished. Took {0}", duration);

        Console.ReadLine();
    }

    private static string ProcessItem(int item)
    {
        // Simulate similar but slightly variable length processing
        int pause = rnd.Next(900, 1100);
        Thread.Sleep(pause);

        return string.Format("Result of item {0}", item);
    }
}

The output of the above code may look something like this:

Basic PLINQ

The result of this code is that it takes roughly 5 second to process the 20 items. I have a 4 core processor so it would be in line with the expectation that the work is distributed across all 4 cores.

Parallelisation Talk Examples – Parallel.ForEach

These are some code examples from my introductory talk on Parallelisation. Showing the difference between a standard sequential foreach loop and its parallel equivalent.

Code example 1: Serial processing of a foreach loop

class Program
{
    private static Random rnd = new Random();

    static void Main(string[] args)
    {
        DateTime start = DateTime.UtcNow;

        IEnumerable items = Enumerable.Range(0,20);

        foreach(int item in items)
            ProcessLoop(item);

        DateTime end = DateTime.UtcNow;
        TimeSpan duration = end - start;

        Console.WriteLine("Finished. Took {0}", duration);

        Console.ReadLine();
    }

    private static void ProcessLoop(int item)
    {
        Console.WriteLine("Processing item {0}", item);

        // Simulate similar but slightly variable length processing
        int pause = rnd.Next(900, 1100);
        Thread.Sleep(pause);
    }
}

The output of the above code may look something like this:

Sequential foreach Example

As you can see this takes roughly of 20 seconds to process 20 items with each item taking about one second to process.

Code Example 2: Parallel processing of a foreach loop

The Parallel class can be found in the System.Threading.Tasks namespace.

class Program
{
    private static Random rnd = new Random();

    static void Main(string[] args)
    {
        DateTime start = DateTime.UtcNow;

        IEnumerable items = Enumerable.Range(0,20);

        Parallel.ForEach(items,
            (item) => ProcessLoop(item));

        DateTime end = DateTime.UtcNow;
        TimeSpan duration = end - start;

        Console.WriteLine("Finished. Took {0}", duration);

        Console.ReadLine();
    }

    private static void ProcessLoop(int item)
    {
        Console.WriteLine("Processing item {0}", item);

        // Simulate similar but slightly variable length processing
        int pause = rnd.Next(900, 1100);
        Thread.Sleep(pause);
    }
}

The output of the above code may look something like this:

Parallel.ForEach Example

The result of this code is that it takes roughly 5 second to process the 20 items. I have a 4 core processor so it would be in line with the expectation that the work is distributed across all 4 cores.