10 Dec 2012

Root the UK Orange/T-Mobile/EE Samsung Galaxy S3 LTE (GT-I9305)

Well, I'm a total Android newbie, having eschewed the little green robot in favour of my fruity 3GS which I've been rocking for 3 years now. But Apple really p1553d me off when they launched iOS6 with crippled maps, and my toleration levels for iTunes crappiness on Windows are at breaking point, AND the new lightning connector means none of my various expensive docks at home and in the car work any more, without a crappy adapter. So basically Apple have gone out of their way to make me try Android out.

My weapon of choice is the new Galaxy S3 LTE - GT-I9305. This international version sports a quad CPU and 2Gb of internal RAM, and Android 4.1 as we got to press, so I went for it over the old GT-I9300 which only has 1Gb RAM and Android 4.0 at this time. The LTE capability was a bonus, and should help resale value, but I don't care about 4G much as I barely get 3G at the best of times. The slinky butter UI on this phone finally convinced me that in terms of display slickness, android is now a match for iOS.

Of course, one of my motivators for breaking free of the iShackles was to be able to root my new phone, stick a custom ROM on it and so become the ULTIMATE GALAXY COMMANDER, whatever that is. Now, the thing I didn't consider when I got this version of the phone is that it's brand spanky new to the android development community, so most of the existing S3 ROMs won't work on it, and many of the tutorials about rooting it seemed GT-I9300 specific. And being a scaredy-cat, I was afraid at each step along this process that I was about to brick my phone / disable the SIM card / explode the modem and basically send me back to the Orange shop with my tail between my legs and 500 quid's worth of broken hardware in my hand.

So if you're like me in that regard, I can at least report that with the following information I rooted my phone without bricking it, and furthermore, all the tools are available that allowed me to test restoring the phone to its stock configuration in case I ever need to send it back. You should note that I'm a Windows user, so this guide isn't much use if you have a mac / linuxbox / oric atmos / whatever.

DISCLAIMER: While the following procedures worked for me, I accept no responsibility for any problems you may encounter. The following advice carries no guarantees.

STEP 1 - Backup to Kies

Kies is Samsung's answer to iTunes for the Galaxy S3, as far as syncing / backing up your phone goes. It's a bit crap to be honest. I was kinda disappointed that it doesn't backup your Apps or App Data. While your Apps are backed up on Google Play automatically, the App Data isn't backed up unless you do it yourself, and for that you need to be rooted.  But your first step along the safe path should be to download Kies and then use the Back Up option to backup everything you want to save to your computer, just in case.

STEP 2 - Install Odin3 on your PC

Odin3 is a fantastic tool that you use on your computer to upload and install special files onto your phone. You will need to have installed Kies to use it with your S3, cos it needs the USB drivers installed. But ensure Kies in not running whenever you use Odin, because they could interfere with each other. Anyway, get Odin here: http://forum.xda-developers.com/showthread.php?t=1738841

STEP 3 - Install ClockWorkMod Recovery on your phone and Root it!

Now, to root my phone I followed a great post on the XDA Developers forum here: http://forum.xda-developers.com/showthread.php?t=1914394 . The gist of it is, you flash (install) the ClockWorkMod (GT-I9305 version) recovery tools onto your phone, and then you flash the SuperSU package to root it.

However, it's worth bearing in mind the following.

  1. As soon as you install CWM Recovery on your phone, it will no longer install OTA (over-the-air) OS updates from your mobile carrier, e.g. when Android 4.2 is finally released for the S3.  So when new ROMs are available you will have to grab the new ROM from somewhere and flash it from Odin instead.
  2. As soon as you install CWM Recovery, it increments the S3's Custom Binary Counter. If you have to send your phone back, the Binary Counter will indicate that you've been tinkering with it. However, there is a solution: ChainFire's TriangleAway App which you can buy on Google Play. And at 2 quid it's a great donation to ChainFire who does a lot of great work for the community, including several of the tools we're using here. I can confirm it works BUT you have to be rooted to use it.
  3. Before you install the root package or start using custom ROMs etc., take a backup of your entire phone and data using CWM's backup feature. Restart the phone and hold VOL UP + POWER + HOME to enter Recovery mode. Choose "backup and restore" and backup to your internal card or external sdcard if you have one. Reboot your phone, hook it up to your computer by USB and simply drag the backup files to your computer. Super useful if you want to restore your stuff later if you go back to the stock firmware.


So here's the thing I was worried about: How to roll the phone back to stock settings from root. For this you'll need a copy of the stock firmware for your phone / country / carrier. I got mine from this amazing site: http://www.sammobile.com/firmware/?page=3&model=GT-I9305&pcode=EVR&os=1&type=1#firmware
and I can confirm that works for a UK Orange S3 LTE.

So, to revert to stock:

  1. Use TriangleAway to reset the Binary Counter.
  2. Put your phone into download mode (restart and hold down VOL DOWN + POWER + HOME)
  3. Use Odin3 to install the stock firmware file you downloaded.

5 Dec 2012

jQuery UI AutoComplete source option - function callback

Just a quick one: if you have a jQuery UI AutoComplete and you want to use the source option to set a function that does a remote lookup, you'll be aware that the function signature is as follows:

function(request, response)

where request is what the user entered, and response is a function reference that accepts an array of results to display.

The prob we had today was that we wanted to call the response function from the success of a $.ajax() call. But it wouldn't work, and it took ages to figure out that this was because the call was in asynchronous mode. Once we set the $.ajax() async option to false, it worked fine.

23 Nov 2012

HP 2311x Monitor - Blurry / Fuzzy on HDMI from AMD radeon HD 7570

Quick one - hooked this 2nd monitor up to my new desktop on the HDMI port of the AMD Radeon HD 7570. The picture was very blurry. It took a while to find the right setting in Windows 7, which was:

  • Right click on desktop, go to Catalyst Control Center
  • Desktop and Displays > My Digital Flat-panels > Scaling Options
  • Turn Scaling down to 0%
  • Return to My Digital Flat-panels
  • Select Properties
  • Click Enable GPU Scaling
Done! What a crappy default!

2 Nov 2012

SQL Server Attach DB : failed to retrieve text for this error. Reason: 15105

Got this error in SSMS when trying to attach an old DB.

failed to retrieve text for this error. Reason: 15105

Quick answer - run Management Studio as Administrator. The error SSMS was giving on attach then displayed correctly, and in this case was a combination of two things:

  • Inadequate permission on the MDF and LDF files
  • Trying to attach a 2008 R2 DB on a plain 2008 server

6 Sept 2012

ASPNETDB ASP.NET Application Services Membership GUI

I'm not a big fan of the ASPNETDB framework - seems needlessly complicated while still not doing many things that you want from your membership system.

But if you have to use it, one thing that really bugs me is that it doesn't have a simple way to remotely manage the users and roles. Here's a tool to lighten the load, a WinForms GUI app: http://sourceforge.net/projects/aspnetdb/

5 Sept 2012

IE, jquery, migrating from live to on for click events

Had a bit of "fun" today migrating from using jquery live() to on(). I had a bit of code like this:

$('.BizCard.selectable').live('click', function () { blah; });

Knowing that we should now use the on() function instead, I changed it to:

$(window).on('click', '.BizCard.selectable', function () { blah; });

This seemed to work OK until I got a bug report that it wasn't working in IE8 and lower. Took a while to work it out, but turns out you can't attach a click handler to the window object in IE before IE9. Therefore the workaround was to attach to document instead:

$(document).on('click', '.BizCard.selectable', function () { blah; });

An aide-memoire on setting cookies, reading cookies with jQuery

Assuming we use jquery.cookie.js, to SET a boolean cookie flag:
$.cookie("AutoShow_" + handle, isExpanding ? 'true' : 'false');
and to read it back
var shouldShow = /true/i.test($.cookie("AutoShow_" + handle));

15 Aug 2012

Allow MVC to work on ASP.NET 4 / IIS7 with runAllManagedModulesForAllRequests set FALSE

In pipeline mode on IIS 7, all requests for content (including requests for static files) play through the ASP.NET handler unless you set runAllManagedModulesForAllRequests to false in the <system .webserver> part of web.config.

Unfortunately by default that stops MVC 3 working because MVC urls do not have a file extension to give IIS a clue what to do.

Well cheer up, cos MS have issued a hotfix. Woop!


For extra info see http://blogs.msdn.com/b/tmarq/archive/2010/04/01/asp-net-4-0-enables-routing-of-extensionless-urls-without-impacting-static-requests.aspx

9 Aug 2012

Visual Studio RegEx for converting old MVC Views to Razor format

This won't fix everything, but does help a lot with converting your old MVC Views to Razor format.

Basically this is RegEx replacement pattern for use in the Visual Studio Find and Replace dialog, for converting out all the old <%: blah %> tags.

Find what:


Replace with:


Don't forget to Use Regular Expressions in the Find Options.

Some of the resultant code won't work until you put brackets round it e.g. @blah blah needs to be @(blah blah)

Don't forget you may need to convert <%= blah %> tags too - perhaps convert them to @Html.Raw(blah)

Don't forget you may need to convert <% blah %> tags too - perhaps convert them to @{blah}

6 Aug 2012

Wii Skylanders USB dongle problem

Let me cut to the chase!

  • The USB dongle has a tiny red LED on it that glows to show it's working. 
  • The USB  ports on your Wii might be dusty - blow them clean.
  • With the Wii on, plug in the dongle and wobble it around in the USB port until its red LED lights up and stays on.
  • Boot up Skylanders, enjoy peace and quiet!
And if you're seriously bored, the backstory:


Phew, almost had a very disappointed birthday kid on my hands there... he got Skylanders for Wii for his birthday which features a plastic castle thing called a Portal Of Power. I didn't realize at first but the Portal contains a usb dongle which you plug into a USB port on the back of the Wii.

Well, I spent about half an hour trying to get the Portal to sync with the dongle, and no dice. The instructions say if the Portal's button is flashing, it hasn't synced with the dongle yet. You're supposed to press the SYNC button in the Portal's battery compartment and then press the button on the dongle to complete the handshake. Well I tried that about a million times but no dice.

Then I figured that the dongle would work on any usb socket so I tried it on my laptop - eureka moment - as soon as I plugged it in, not only did the Portal's button light go solid (indicating successful sync), but a little red light on the end of the dongle lit up. I didn't know the dongle had a light! So I started a-thinking that maybe the porblem was the usb ports on the wii.

I don't know about you, but i've had my Wii for about 5 years and have never used the USB ports before. They're open and collect dust so I blew them out. I plugged the dongle in - no red light. I wiggled the dongle around - RED LIGHT! I wiggled some more - no red light.

Well, seems like the USB ports on my Wii are a bit flakey. After a while I wiggled the dongle into a position where the light stayed on, and then gingerly left it alone. Booted up Skylanders and we got past the Sync screen for the first time.

WOOHOO - kid is happy and I can get on with my "real" job.

1 Aug 2012

Making Asynchronous / Concurrent calls to MVC - requests are synchronized

After a while coding in ASP.NET, I took some things for granted - the Session object for example. It's always there, so useful for storing little tidbits of stuff.

But I never pondered on the DARK SIDE OF THE SESSION, as regards its implication in MVC. Yes, I was vaguely aware that access to session data had to be synchronized (i.e. an exclusive lock placed on the session's data during access) but I didn't figure the duration of the lock. It turns out that in regular use, all MVC Controller Actions lock Session access for the duration of the request, with the reasoning that Session access could occur at any time during the request.

Therefore, if your browser makes several concurrent MVC requests - perhaps a regular View call, a polling AJAX call and an Image-Generation call - they will be run sequentially, NOT simultaneously.

Therefore, if one of your calls is slow to return, the others in the queue are held up in turn!

In old-school ASP.NET, you could turn this off at the Page level by setting EnableSessionState="false" in the @Page directive. But up til version 3, there was no equivalent in MVC apart from the radical step of disabling SessionState in the web.config file. See this post for more info on those strategies.

Well, in MVC 3 we still don't have fine-grained Sesssion control at the Action level, but we do at the Controller level, with the SessionState attribute. This allows us to say if we want to use default behaviour, ReadOnly access (concurrent reads, synchronized writes), or fully disabled Session in our Controller's Actions. With Session Disabled or ReadOnly, we have the asynchronous request ability that we desire, albeit that we have to be careful we make no calls (or at least Writes, in ReadOnly mode) to Session now that we have restricted access.

Usage is like so:

    public class DocumentController : Controller
        public ActionResult ViewDocument(int id)
            var doc = WebApp.MainService.GetDocument(id);

            if (doc == null 
                || !(doc.UploadedBy == WebApp.CurrentUser.UserId 
                    || doc.OwnerUserId == WebApp.CurrentUser.UserId 
                    || WebApp.CurrentUser.IsInRole(Core.Enums.RoleType.Admin, WebApp.CurrentSite.SiteId)))
                throw new Exception("You do not have permission to view this Document");

            ContentDisposition cd = new ContentDisposition { Inline = true, FileName = doc.Filename };
            Response.AppendHeader("Content-Disposition", cd.ToString());
            return File(DocumentService.GetSavedDocumentFileName(doc), FileTools.DetermineContentType(doc.Filename));

2 Jul 2012

A Kick-Ass Country Selector List / Country DropDownList for MVC 3 with Razor

Country List selectors on website generally suck. They especially suck when you use a UK website and "United Kingdom" is naturally at the bottom of the list because of boring old alpha-sort. What you'd ideally want is to start typing in a Country box, and the most relevant countries should appear at the top of the list.

Well, Jamie Appleseed at the Baymard Institute has a solution, which rocketh most awesomely. It's so cool it does stuff like return "Germany" at the top of the list when the user starts typing "Deutschland". That's so USABLE I want to take the code home and smooch with it all night long.

Here I provide a way to use it in MVC 3 / Razor, so you can call it very simply from your Views.

1. Get the jquery.select-to-autocomplete.js library off github and save it in your MVC site's Scripts folder.

2. Include a call to the library from your masterpage

<script src="@Url.Content("~/Scripts/jquery.selectToAutocomplete.js")" type="text/javascript"></script>

3. In your Views / Shared / EditorTemplates folder, create a file called CountryList.cshtml and stick the following in it.

@model string

    var controlId = ViewData.TemplateInfo.HtmlFieldPrefix.Replace(".", HtmlHelper.IdAttributeDotReplacement);

<script id="scriptCountryList" language="javascript">

    $(function () {

        if ($('#@controlId').val() != '@Model')



<select name="@ViewData.TemplateInfo.HtmlFieldPrefix" id="@controlId" autofocus="autofocus" autocorrect="off" autocomplete="off">
    <option value=""></option>
    <option value="Afghanistan" data-alternative-spellings="AF افغانستان">Afghanistan</option>
    <option value="Åland Islands" data-alternative-spellings="AX Aaland Aland" data-relevancy-booster="0.5">Åland Islands</option>
    <option value="Albania" data-alternative-spellings="AL">Albania</option>
    <option value="Algeria" data-alternative-spellings="DZ الجزائر">Algeria</option>
    <option value="American Samoa" data-alternative-spellings="AS" data-relevancy-booster="0.5">American Samoa</option>
    <option value="Andorra" data-alternative-spellings="AD" data-relevancy-booster="0.5">Andorra</option>
    <option value="Angola" data-alternative-spellings="AO">Angola</option>
    <option value="Anguilla" data-alternative-spellings="AI" data-relevancy-booster="0.5">Anguilla</option>
    <option value="Antarctica" data-alternative-spellings="AQ" data-relevancy-booster="0.5">Antarctica</option>
    <option value="Antigua And Barbuda" data-alternative-spellings="AG" data-relevancy-booster="0.5">Antigua And Barbuda</option>
    <option value="Argentina" data-alternative-spellings="AR">Argentina</option>
    <option value="Armenia" data-alternative-spellings="AM Հայաստան">Armenia</option>
    <option value="Aruba" data-alternative-spellings="AW" data-relevancy-booster="0.5">Aruba</option>
    <option value="Australia" data-alternative-spellings="AU" data-relevancy-booster="1.5">Australia</option>
    <option value="Austria" data-alternative-spellings="AT Österreich Osterreich Oesterreich ">Austria</option>
    <option value="Azerbaijan" data-alternative-spellings="AZ">Azerbaijan</option>
    <option value="Bahamas" data-alternative-spellings="BS">Bahamas</option>
    <option value="Bahrain" data-alternative-spellings="BH البحرين">Bahrain</option>
    <option value="Bangladesh" data-alternative-spellings="BD বাংলাদেশ" data-relevancy-booster="2">Bangladesh</option>
    <option value="Barbados" data-alternative-spellings="BB">Barbados</option>
    <option value="Belarus" data-alternative-spellings="BY Беларусь">Belarus</option>
    <option value="Belgium" data-alternative-spellings="BE België Belgie Belgien Belgique" data-relevancy-booster="1.5">Belgium</option>
    <option value="Belize" data-alternative-spellings="BZ">Belize</option>
    <option value="Benin" data-alternative-spellings="BJ">Benin</option>
    <option value="Bermuda" data-alternative-spellings="BM" data-relevancy-booster="0.5">Bermuda</option>
    <option value="Bhutan" data-alternative-spellings="BT भूटान">Bhutan</option>
    <option value="Bolivia" data-alternative-spellings="BO">Bolivia</option>
    <option value="Bonaire, Sint Eustatius and Saba" data-alternative-spellings="BQ">Bonaire, Sint Eustatius and Saba</option>
    <option value="Bosnia and Herzegovina" data-alternative-spellings="BA Босна и Херцеговина">Bosnia and Herzegovina</option>
    <option value="Botswana" data-alternative-spellings="BW">Botswana</option>
    <option value="Bouvet Island" data-alternative-spellings="BV">Bouvet Island</option>
    <option value="Brazil" data-alternative-spellings="BR Brasil" data-relevancy-booster="2">Brazil</option>
    <option value="British Indian Ocean Territory" data-alternative-spellings="IO">British Indian Ocean Territory</option>
    <option value="Brunei Darussalam" data-alternative-spellings="BN">Brunei Darussalam</option>
    <option value="Bulgaria" data-alternative-spellings="BG България">Bulgaria</option>
    <option value="Burkina Faso" data-alternative-spellings="BF">Burkina Faso</option>
    <option value="Burundi" data-alternative-spellings="BI">Burundi</option>
    <option value="Cambodia" data-alternative-spellings="KH កម្ពុជា">Cambodia</option>
    <option value="Cameroon" data-alternative-spellings="CM">Cameroon</option>
    <option value="Canada" data-alternative-spellings="CA" data-relevancy-booster="2">Canada</option>
    <option value="Cape Verde" data-alternative-spellings="CV Cabo">Cape Verde</option>
    <option value="Cayman Islands" data-alternative-spellings="KY" data-relevancy-booster="0.5">Cayman Islands</option>
    <option value="Central African Republic" data-alternative-spellings="CF">Central African Republic</option>
    <option value="Chad" data-alternative-spellings="TD تشاد‎ Tchad">Chad</option>
    <option value="Chile" data-alternative-spellings="CL">Chile</option>
    <option value="China" data-relevancy-booster="3.5" data-alternative-spellings="CN Zhongguo Zhonghua Peoples Republic 中国/中华">China</option>
    <option value="Christmas Island" data-alternative-spellings="CX" data-relevancy-booster="0.5">Christmas Island</option>
    <option value="Cocos (Keeling) Islands" data-alternative-spellings="CC" data-relevancy-booster="0.5">Cocos (Keeling) Islands</option>
    <option value="Colombia" data-alternative-spellings="CO">Colombia</option>
    <option value="Comoros" data-alternative-spellings="KM جزر القمر">Comoros</option>
    <option value="Congo" data-alternative-spellings="CG">Congo</option>
    <option value="Congo, the Democratic Republic of the" data-alternative-spellings="CD Congo-Brazzaville Repubilika ya Kongo">Congo, the Democratic Republic of the</option>
    <option value="Cook Islands" data-alternative-spellings="CK" data-relevancy-booster="0.5">Cook Islands</option>
    <option value="Costa Rica" data-alternative-spellings="CR">Costa Rica</option>
    <option value="Côte d'Ivoire" data-alternative-spellings="CI Cote dIvoire">Côte d'Ivoire</option>
    <option value="Croatia" data-alternative-spellings="HR Hrvatska">Croatia</option>
    <option value="Cuba" data-alternative-spellings="CU">Cuba</option>
    <option value="Curaçao" data-alternative-spellings="CW Curacao">Curaçao</option>
    <option value="Cyprus" data-alternative-spellings="CY Κύπρος Kýpros Kıbrıs">Cyprus</option>
    <option value="Czech Republic" data-alternative-spellings="CZ Česká Ceska">Czech Republic</option>
    <option value="Denmark" data-alternative-spellings="DK Danmark" data-relevancy-booster="1.5">Denmark</option>
    <option value="Djibouti" data-alternative-spellings="DJ جيبوتي‎ Jabuuti Gabuuti">Djibouti</option>
    <option value="Dominica" data-alternative-spellings="DM Dominique" data-relevancy-booster="0.5">Dominica</option>
    <option value="Dominican Republic" data-alternative-spellings="DO">Dominican Republic</option>
    <option value="Ecuador" data-alternative-spellings="EC">Ecuador</option>
    <option value="Egypt" data-alternative-spellings="EG" data-relevancy-booster="1.5">Egypt</option>
    <option value="El Salvador" data-alternative-spellings="SV">El Salvador</option>
    <option value="Equatorial Guinea" data-alternative-spellings="GQ">Equatorial Guinea</option>
    <option value="Eritrea" data-alternative-spellings="ER إرتريا ኤርትራ">Eritrea</option>
    <option value="Estonia" data-alternative-spellings="EE Eesti">Estonia</option>
    <option value="Ethiopia" data-alternative-spellings="ET ኢትዮጵያ">Ethiopia</option>
    <option value="Falkland Islands (Malvinas)" data-alternative-spellings="FK" data-relevancy-booster="0.5">Falkland Islands (Malvinas)</option>
    <option value="Faroe Islands" data-alternative-spellings="FO Føroyar Færøerne" data-relevancy-booster="0.5">Faroe Islands</option>
    <option value="Fiji" data-alternative-spellings="FJ Viti फ़िजी">Fiji</option>
    <option value="Finland" data-alternative-spellings="FI Suomi">Finland</option>
    <option value="France" data-alternative-spellings="FR République française" data-relevancy-booster="2.5">France</option>
    <option value="French Guiana" data-alternative-spellings="GF">French Guiana</option>
    <option value="French Polynesia" data-alternative-spellings="PF Polynésie française">French Polynesia</option>
    <option value="French Southern Territories" data-alternative-spellings="TF">French Southern Territories</option>
    <option value="Gabon" data-alternative-spellings="GA République Gabonaise">Gabon</option>
    <option value="Gambia" data-alternative-spellings="GM">Gambia</option>
    <option value="Georgia" data-alternative-spellings="GE საქართველო">Georgia</option>
    <option value="Germany" data-alternative-spellings="DE Bundesrepublik Deutschland" data-relevancy-booster="3">Germany</option>
    <option value="Ghana" data-alternative-spellings="GH">Ghana</option>
    <option value="Gibraltar" data-alternative-spellings="GI" data-relevancy-booster="0.5">Gibraltar</option>
    <option value="Greece" data-alternative-spellings="GR Ελλάδα" data-relevancy-booster="1.5">Greece</option>
    <option value="Greenland" data-alternative-spellings="GL grønland" data-relevancy-booster="0.5">Greenland</option>
    <option value="Grenada" data-alternative-spellings="GD">Grenada</option>
    <option value="Guadeloupe" data-alternative-spellings="GP">Guadeloupe</option>
    <option value="Guam" data-alternative-spellings="GU">Guam</option>
    <option value="Guatemala" data-alternative-spellings="GT">Guatemala</option>
    <option value="Guernsey" data-alternative-spellings="GG" data-relevancy-booster="0.5">Guernsey</option>
    <option value="Guinea" data-alternative-spellings="GN">Guinea</option>
    <option value="Guinea-Bissau" data-alternative-spellings="GW">Guinea-Bissau</option>
    <option value="Guyana" data-alternative-spellings="GY">Guyana</option>
    <option value="Haiti" data-alternative-spellings="HT">Haiti</option>
    <option value="Heard Island and McDonald Islands" data-alternative-spellings="HM">Heard Island and McDonald Islands</option>
    <option value="Holy See (Vatican City State)" data-alternative-spellings="VA" data-relevancy-booster="0.5">Holy See (Vatican City State)</option>
    <option value="Honduras" data-alternative-spellings="HN">Honduras</option>
    <option value="Hong Kong" data-alternative-spellings="HK 香港">Hong Kong</option>
    <option value="Hungary" data-alternative-spellings="HU Magyarország">Hungary</option>
    <option value="Iceland" data-alternative-spellings="IS Island">Iceland</option>
    <option value="India" data-alternative-spellings="IN भारत गणराज्य Hindustan" data-relevancy-booster="3">India</option>
    <option value="Indonesia" data-alternative-spellings="ID" data-relevancy-booster="2">Indonesia</option>
    <option value="Iran, Islamic Republic of" data-alternative-spellings="IR ایران">Iran, Islamic Republic of</option>
    <option value="Iraq" data-alternative-spellings="IQ العراق‎">Iraq</option>
    <option value="Ireland" data-alternative-spellings="IE Éire" data-relevancy-booster="1.2">Ireland</option>
    <option value="Isle of Man" data-alternative-spellings="IM" data-relevancy-booster="0.5">Isle of Man</option>
    <option value="Israel" data-alternative-spellings="IL إسرائيل ישראל">Israel</option>
    <option value="Italy" data-alternative-spellings="IT Italia" data-relevancy-booster="2">Italy</option>
    <option value="Jamaica" data-alternative-spellings="JM">Jamaica</option>
    <option value="Japan" data-alternative-spellings="JP Nippon Nihon 日本" data-relevancy-booster="2.5">Japan</option>
    <option value="Jersey" data-alternative-spellings="JE" data-relevancy-booster="0.5">Jersey</option>
    <option value="Jordan" data-alternative-spellings="JO الأردن">Jordan</option>
    <option value="Kazakhstan" data-alternative-spellings="KZ Қазақстан Казахстан">Kazakhstan</option>
    <option value="Kenya" data-alternative-spellings="KE">Kenya</option>
    <option value="Kiribati" data-alternative-spellings="KI">Kiribati</option>
    <option value="Korea, Democratic People's Republic of" data-alternative-spellings="KP North Korea">Korea, Democratic People's Republic of</option>
    <option value="Korea, Republic of" data-alternative-spellings="KR South Korea" data-relevancy-booster="1.5">Korea, Republic of</option>
    <option value="Kuwait" data-alternative-spellings="KW الكويت">Kuwait</option>
    <option value="Kyrgyzstan" data-alternative-spellings="KG Кыргызстан">Kyrgyzstan</option>
    <option value="Lao People's Democratic Republic" data-alternative-spellings="LA">Lao People's Democratic Republic</option>
    <option value="Latvia" data-alternative-spellings="LV Latvija">Latvia</option>
    <option value="Lebanon" data-alternative-spellings="LB لبنان">Lebanon</option>
    <option value="Lesotho" data-alternative-spellings="LS">Lesotho</option>
    <option value="Liberia" data-alternative-spellings="LR">Liberia</option>
    <option value="Libyan Arab Jamahiriya" data-alternative-spellings="LY ليبيا">Libyan Arab Jamahiriya</option>
    <option value="Liechtenstein" data-alternative-spellings="LI">Liechtenstein</option>
    <option value="Lithuania" data-alternative-spellings="LT Lietuva">Lithuania</option>
    <option value="Luxembourg" data-alternative-spellings="LU">Luxembourg</option>
    <option value="Macao" data-alternative-spellings="MO">Macao</option>
    <option value="Macedonia, The Former Yugoslav Republic Of" data-alternative-spellings="MK Македонија">Macedonia, The Former Yugoslav Republic Of</option>
    <option value="Madagascar" data-alternative-spellings="MG Madagasikara">Madagascar</option>
    <option value="Malawi" data-alternative-spellings="MW">Malawi</option>
    <option value="Malaysia" data-alternative-spellings="MY">Malaysia</option>
    <option value="Maldives" data-alternative-spellings="MV">Maldives</option>
    <option value="Mali" data-alternative-spellings="ML">Mali</option>
    <option value="Malta" data-alternative-spellings="MT">Malta</option>
    <option value="Marshall Islands" data-alternative-spellings="MH" data-relevancy-booster="0.5">Marshall Islands</option>
    <option value="Martinique" data-alternative-spellings="MQ">Martinique</option>
    <option value="Mauritania" data-alternative-spellings="MR الموريتانية">Mauritania</option>
    <option value="Mauritius" data-alternative-spellings="MU">Mauritius</option>
    <option value="Mayotte" data-alternative-spellings="YT">Mayotte</option>
    <option value="Mexico" data-alternative-spellings="MX Mexicanos" data-relevancy-booster="1.5">Mexico</option>
    <option value="Micronesia, Federated States of" data-alternative-spellings="FM">Micronesia, Federated States of</option>
    <option value="Moldova, Republic of" data-alternative-spellings="MD">Moldova, Republic of</option>
    <option value="Monaco" data-alternative-spellings="MC">Monaco</option>
    <option value="Mongolia" data-alternative-spellings="MN Mongγol ulus Монгол улс">Mongolia</option>
    <option value="Montenegro" data-alternative-spellings="ME">Montenegro</option>
    <option value="Montserrat" data-alternative-spellings="MS" data-relevancy-booster="0.5">Montserrat</option>
    <option value="Morocco" data-alternative-spellings="MA المغرب">Morocco</option>
    <option value="Mozambique" data-alternative-spellings="MZ Moçambique">Mozambique</option>
    <option value="Myanmar" data-alternative-spellings="MM">Myanmar</option>
    <option value="Namibia" data-alternative-spellings="NA Namibië">Namibia</option>
    <option value="Nauru" data-alternative-spellings="NR Naoero" data-relevancy-booster="0.5">Nauru</option>
    <option value="Nepal" data-alternative-spellings="NP नेपाल">Nepal</option>
    <option value="Netherlands" data-alternative-spellings="NL Holland Nederland" data-relevancy-booster="1.5">Netherlands</option>
    <option value="New Caledonia" data-alternative-spellings="NC" data-relevancy-booster="0.5">New Caledonia</option>
    <option value="New Zealand" data-alternative-spellings="NZ Aotearoa">New Zealand</option>
    <option value="Nicaragua" data-alternative-spellings="NI">Nicaragua</option>
    <option value="Niger" data-alternative-spellings="NE Nijar">Niger</option>
    <option value="Nigeria" data-alternative-spellings="NG Nijeriya Naíjíríà" data-relevancy-booster="1.5">Nigeria</option>
    <option value="Niue" data-alternative-spellings="NU" data-relevancy-booster="0.5">Niue</option>
    <option value="Norfolk Island" data-alternative-spellings="NF" data-relevancy-booster="0.5">Norfolk Island</option>
    <option value="Northern Mariana Islands" data-alternative-spellings="MP" data-relevancy-booster="0.5">Northern Mariana Islands</option>
    <option value="Norway" data-alternative-spellings="NO Norge Noreg" data-relevancy-booster="1.5">Norway</option>
    <option value="Oman" data-alternative-spellings="OM عمان">Oman</option>
    <option value="Pakistan" data-alternative-spellings="PK پاکستان" data-relevancy-booster="2">Pakistan</option>
    <option value="Palau" data-alternative-spellings="PW" data-relevancy-booster="0.5">Palau</option>
    <option value="Palestinian Territory, Occupied" data-alternative-spellings="PS فلسطين">Palestinian Territory, Occupied</option>
    <option value="Panama" data-alternative-spellings="PA">Panama</option>
    <option value="Papua New Guinea" data-alternative-spellings="PG">Papua New Guinea</option>
    <option value="Paraguay" data-alternative-spellings="PY">Paraguay</option>
    <option value="Peru" data-alternative-spellings="PE">Peru</option>
    <option value="Philippines" data-alternative-spellings="PH Pilipinas" data-relevancy-booster="1.5">Philippines</option>
    <option value="Pitcairn" data-alternative-spellings="PN" data-relevancy-booster="0.5">Pitcairn</option>
    <option value="Poland" data-alternative-spellings="PL Polska" data-relevancy-booster="1.25">Poland</option>
    <option value="Portugal" data-alternative-spellings="PT Portuguesa" data-relevancy-booster="1.5">Portugal</option>
    <option value="Puerto Rico" data-alternative-spellings="PR">Puerto Rico</option>
    <option value="Qatar" data-alternative-spellings="QA قطر">Qatar</option>
    <option value="Réunion" data-alternative-spellings="RE Reunion">Réunion</option>
    <option value="Romania" data-alternative-spellings="RO Rumania Roumania România">Romania</option>
    <option value="Russian Federation" data-alternative-spellings="RU Rossiya Российская Россия" data-relevancy-booster="2.5">Russian Federation</option>
    <option value="Rwanda" data-alternative-spellings="RW">Rwanda</option>
    <option value="Saint Barthélemy" data-alternative-spellings="BL St. Barthelemy">Saint Barthélemy</option>
    <option value="Saint Helena" data-alternative-spellings="SH St.">Saint Helena</option>
    <option value="Saint Kitts and Nevis" data-alternative-spellings="KN St.">Saint Kitts and Nevis</option>
    <option value="Saint Lucia" data-alternative-spellings="LC St.">Saint Lucia</option>
    <option value="Saint Martin (French Part)" data-alternative-spellings="MF St.">Saint Martin (French Part)</option>
    <option value="Saint Pierre and Miquelon" data-alternative-spellings="PM St.">Saint Pierre and Miquelon</option>
    <option value="Saint Vincent and the Grenadines" data-alternative-spellings="VC St.">Saint Vincent and the Grenadines</option>
    <option value="Samoa" data-alternative-spellings="WS">Samoa</option>
    <option value="San Marino" data-alternative-spellings="SM">San Marino</option>
    <option value="Sao Tome and Principe" data-alternative-spellings="ST">Sao Tome and Principe</option>
    <option value="Saudi Arabia" data-alternative-spellings="SA السعودية">Saudi Arabia</option>
    <option value="Senegal" data-alternative-spellings="SN Sénégal">Senegal</option>
    <option value="Serbia" data-alternative-spellings="RS Србија Srbija">Serbia</option>
    <option value="Seychelles" data-alternative-spellings="SC" data-relevancy-booster="0.5">Seychelles</option>
    <option value="Sierra Leone" data-alternative-spellings="SL">Sierra Leone</option>
    <option value="Singapore" data-alternative-spellings="SG Singapura  சிங்கப்பூர் குடியரசு 新加坡共和国">Singapore</option>
    <option value="Sint Maarten (Dutch Part)" data-alternative-spellings="SX">Sint Maarten (Dutch Part)</option>
    <option value="Slovakia" data-alternative-spellings="SK Slovenská Slovensko">Slovakia</option>
    <option value="Slovenia" data-alternative-spellings="SI Slovenija">Slovenia</option>
    <option value="Solomon Islands" data-alternative-spellings="SB">Solomon Islands</option>
    <option value="Somalia" data-alternative-spellings="SO الصومال">Somalia</option>
    <option value="South Africa" data-alternative-spellings="ZA RSA Suid-Afrika">South Africa</option>
    <option value="South Georgia and the South Sandwich Islands" data-alternative-spellings="GS">South Georgia and the South Sandwich Islands</option>
    <option value="South Sudan" data-alternative-spellings="SS">South Sudan</option>
    <option value="Spain" data-alternative-spellings="ES España" data-relevancy-booster="2">Spain</option>
    <option value="Sri Lanka" data-alternative-spellings="LK ශ්‍රී ලංකා இலங்கை Ceylon">Sri Lanka</option>
    <option value="Sudan" data-alternative-spellings="SD السودان">Sudan</option>
    <option value="Suriname" data-alternative-spellings="SR शर्नम् Sarnam Sranangron">Suriname</option>
    <option value="Svalbard and Jan Mayen" data-alternative-spellings="SJ" data-relevancy-booster="0.5">Svalbard and Jan Mayen</option>
    <option value="Swaziland" data-alternative-spellings="SZ weSwatini Swatini Ngwane">Swaziland</option>
    <option value="Sweden" data-alternative-spellings="SE Sverige" data-relevancy-booster="1.5">Sweden</option>
    <option value="Switzerland" data-alternative-spellings="CH Swiss Confederation Schweiz Suisse Svizzera Svizra" data-relevancy-booster="1.5">Switzerland</option>
    <option value="Syrian Arab Republic" data-alternative-spellings="SY Syria سورية">Syrian Arab Republic</option>
    <option value="Taiwan, Province of China" data-alternative-spellings="TW 台灣 臺灣">Taiwan, Province of China</option>
    <option value="Tajikistan" data-alternative-spellings="TJ Тоҷикистон Toçikiston">Tajikistan</option>
    <option value="Tanzania, United Republic of" data-alternative-spellings="TZ">Tanzania, United Republic of</option>
    <option value="Thailand" data-alternative-spellings="TH ประเทศไทย Prathet Thai">Thailand</option>
    <option value="Timor-Leste" data-alternative-spellings="TL">Timor-Leste</option>
    <option value="Togo" data-alternative-spellings="TG Togolese">Togo</option>
    <option value="Tokelau" data-alternative-spellings="TK" data-relevancy-booster="0.5">Tokelau</option>
    <option value="Tonga" data-alternative-spellings="TO">Tonga</option>
    <option value="Trinidad and Tobago" data-alternative-spellings="TT">Trinidad and Tobago</option>
    <option value="Tunisia" data-alternative-spellings="TN تونس">Tunisia</option>
    <option value="Turkey" data-alternative-spellings="TR Türkiye Turkiye">Turkey</option>
    <option value="Turkmenistan" data-alternative-spellings="TM Türkmenistan">Turkmenistan</option>
    <option value="Turks and Caicos Islands" data-alternative-spellings="TC" data-relevancy-booster="0.5">Turks and Caicos Islands</option>
    <option value="Tuvalu" data-alternative-spellings="TV" data-relevancy-booster="0.5">Tuvalu</option>
    <option value="Uganda" data-alternative-spellings="UG">Uganda</option>
    <option value="Ukraine" data-alternative-spellings="UA Ukrayina Україна">Ukraine</option>
    <option value="United Arab Emirates" data-alternative-spellings="AE UAE الإمارات">United Arab Emirates</option>
    <option value="United Kingdom" data-alternative-spellings="GB Great Britain England UK Wales Scotland Northern Ireland" data-relevancy-booster="2.5">United Kingdom</option>
    <option value="United States" data-relevancy-booster="3.5" data-alternative-spellings="US USA United States of America">United States</option>
    <option value="United States Minor Outlying Islands" data-alternative-spellings="UM">United States Minor Outlying Islands</option>
    <option value="Uruguay" data-alternative-spellings="UY">Uruguay</option>
    <option value="Uzbekistan" data-alternative-spellings="UZ Ўзбекистон O'zbekstan O‘zbekiston">Uzbekistan</option>
    <option value="Vanuatu" data-alternative-spellings="VU">Vanuatu</option>
    <option value="Venezuela" data-alternative-spellings="VE">Venezuela</option>
    <option value="Vietnam" data-alternative-spellings="VN Việt Nam" data-relevancy-booster="1.5">Vietnam</option>
    <option value="Virgin Islands, British" data-alternative-spellings="VG" data-relevancy-booster="0.5">Virgin Islands, British</option>
    <option value="Virgin Islands, U.S." data-alternative-spellings="VI" data-relevancy-booster="0.5">Virgin Islands, U.S.</option>
    <option value="Wallis and Futuna" data-alternative-spellings="WF" data-relevancy-booster="0.5">Wallis and Futuna</option>
    <option value="Western Sahara" data-alternative-spellings="EH لصحراء الغربية">Western Sahara</option>
    <option value="Yemen" data-alternative-spellings="YE اليمن">Yemen</option>
    <option value="Zambia" data-alternative-spellings="ZM">Zambia</option>
    <option value="Zimbabwe" data-alternative-spellings="ZW">Zimbabwe</option>
4. Simply call the CountryList from your view using the EditorFor HtmlHelper :

@Html.EditorFor(model => model.UserCountry, "CountryList")

or even better, use the UIHint attribute (in System.ComponentModel.DataAnnotations) to decorate your model to use the CountryList automatically.

So in your model:

[Required(AllowEmptyStrings = false)]
[Display(Name = "Country")]
public string UserCountry { get; set; }

and in your view, a beautifully simple call:

@Html.EditorFor(model => model.UserCountry)

28 Jun 2012

Access Denied on the Amazon MWS Scratchpad

Annoying! I was getting an Access Denied error when sending through requests on the Amazon MWS Scratchpad. I checked and rechecked I was sending through the correct Access Key, Secret Key, Marketplace ID etc.

Turns out I was on the wrong site! My Marketplace was in the UK so I have to use




20 Jun 2012

A custom ValueInjecter Injection to use with models decorated with the MVC Bind Attribute

In some cases, I would like to use the Omu ValueInjecter's InjectFrom() to only copy source properties which have been marked for inclusion by an MVC BindAttribute. So, I've developed the following Injection:

public class BindAwareInjection : LoopValueInjection
    public List<string> PropertiesToInclude = new List<string>();
    public List<string> PropertiesToExclude = new List<string>();
    public BindRuleLocationType BindRuleLocation = BindRuleLocationType.Source;

    public enum BindRuleLocationType

    protected override void Inject(object source, object target)
        var bindRuleObject = BindRuleLocation == BindRuleLocationType.Source ? source : target;

        var bindAttributes = (BindAttribute[])bindRuleObject.GetType().GetCustomAttributes(typeof(BindAttribute), true);
        foreach (var bindRule in bindAttributes)
            if (!string.IsNullOrEmpty(bindRule.Include))
                PropertiesToInclude = new List<string>(bindRule.Include.Split(','));
            else if (!string.IsNullOrEmpty(bindRule.Exclude))

        base.Inject(source, target);

    protected override bool UseSourceProp(string sourcePropName)
        if (PropertiesToInclude.Count > 0)
            return PropertiesToExclude.Any(prop => prop == sourcePropName);
        else if (!PropertiesToExclude.Any(prop => prop == sourcePropName))
            return true;
        return false;

This allows me to simply define ViewModels with Bind Exclusion lists like this:

public class User{
    public bool IsSuperUser { get; set; }
    public string Name { get; set; }

[Bind(Exclude = "IsSuperUser")]
public class UserVM : User { }

And then my Controller's Update Action would look like this:

public ActionResult UpdateUser(UserVM model)
    User target = Repository.GetCurrentUser();
    return View(target);

Using that technique, the end user can update the User object's Name property without being able to overwrite the IsSuperUser property.


It's true that MVC's UpdateModel() method obeys BindAttribute inclusion/exclusions found on the target object, so you may ask why we need to use ValueInjecter at all. However, that would necessitate the target object type to contain the Bind attributes. In our case, we want the source and target to be of different types, so that the Source (ViewModel) type has the Bind exclusions, leaving the Target type (Source's super-type) undecorated. This approach allows us to declare thin ViewModels based on underlying domain classes without having to slavishly modify our VM definitions every time the superclass definition changes.

Incidentally, by default the BindAwareInjection uses the Bind attributes from the Source, but you can use the Target attributes instead like this:

public ActionResult UpdateUser(UserVM model)
    var target = Repository.GetCurrentUser();
    var injection = new BindAwareInjection { BindRuleLocation = BindAwareInjection.BindRuleLocationType.Target };
    target.InjectFrom(injection, model);
    return View(target);

MVC Bind Attribute

Just a quick tip. If you want to use the Bind attribute to set the binding whitelist/blacklist (include/exclude) settings, you may have seen many examples where they set the attribute in the Controller Action signature i.e.
public ActionResult UpdateThing([Bind(Exclude="IsSuperUser")]UserEntity model)

However, I think the following approach is nicer. First define your ViewModel, which simply extends your entity model. Note that we define the Bind Attribute on the class definition itself.

public class VM_User : UserEntity {}

Then you just bind from the ViewModel in your Action.

public ActionResult UpdateThing(VM_User model)
To copy property data back from your ViewModel to your entity model, I favour ValueInjecter. I wrote a special Injection class for this purpose: http://zootfroot.blogspot.co.uk/2012/06/custom-valueinjecter-injection-to-use.html

12 Jun 2012

Disappointed with Raspberry Pi

Thought for the day - what's so great about the Raspberry Pi? We've had one in the office for a week and we're already bored of it.

It's not the Pi's fault, either. For me, the problem is one of ridiculous expectations. The talk has all been of a computer that was going to reinvigorate the teaching of computer science and give modern kids an inroad into the subject.

To me, that suggested that the Pi was going to be simple, as in 'so boneheaded that even I can understand how it works'. But the Pi is far from simple. It's a very cleverly miniaturised PC on a single board. Everything on the board is tiny. I really don't think the fact that the guts are exposed will help kids learn what they do, why and how.

Then when you turn it on, it's a regular PC running Linux, only running it a bit slow because it's not very powerful. Again, I don't see how this helps kids out. Linux is a powerful and complicated operating system. A kid can't learn why we need an OS, or how it works under the hood, by just booting Linux.

I guess the thing that blew peoples minds about the Pi is the price. For 20 quid, it is excellent value, for a slightly dozy linux box. But I don't believe the problem that needs solving is one of expense.

Rather, what I was REALLY hoping for was something with the instant feedback that I used to enjoy in the 1980s with my 8-bit machines. I learned to program at an early age because when you turned the computer on (Apple II and ZX Spectrum 48k in my case) it would just sit there with a blinking cursor. You typed your basic program right in on the command line, and ran it.

20 GOTO 10

To achieve the same effect on a modern machine requires booting up, logging in, loading a development IDE, building and running. Layers of abstraction and complication. The modern OS multitasks, runs your little program in a GUI window. In my opinion, it's too much. The good old days of the black screen and the flashing cursor put you right in control. You could build up your understanding slowly.

Regarding the hardware, I would have loved to have understood exactly how my computer worked at a deep level even back in the 80s. I don't know where kids are going to get that low-level experience now. Playing with Arduino-type hardware might be a way, but it still needs programming. I seem to remember playing with physical logic gate boards at school, which I enjoyed - physical programming. I think teaching electronics at school would be fantastic - capacitors, diodes, circuit boards.

Getting back to the roots has to be the way to learn!

Sending email with inline images in ASP.NET MVC

To use inline images in your email, your mail should use HTML format and refer to the images using the collection of Linked Resources, like so.

using (var client = new SmtpClient())
    MailMessage newMail = new MailMessage();
    newMail.To.Add(new MailAddress("you@your.address"));
    newMail.Subject = "Test Subject";
    newMail.IsBodyHtml = true;

    var inlineLogo = new LinkedResource(Server.MapPath("~/Path/To/YourImage.png"));
    inlineLogo.ContentId = Guid.NewGuid().ToString();

    string body = string.Format(@"
            <p>Lorum Ipsum Blah Blah</p>
            <img src=""cid:{0}"" />
            <p>Lorum Ipsum Blah Blah</p>
        ", inlineLogo.ContentId);

    var view = AlternateView.CreateAlternateViewFromString(body, null, "text/html");


The benefit of this approach over the use of the Attachments collection is that the linked images do not appear in the mail client's list of attachments for the mail.

6 Jun 2012

Override the ASP.NET MVC 3 Nullable Boolean EditorFor EditorTemplate

If you use EditorFor() in your MVC View code and pass it a nullable boolean (bool?), you'll see that MVC helpfully renders a tri-state dropdown list. However, the three choices in the dropdown are "Not Set", "True", and "False" which seem a bit unfriendly to some users.

Save the following template as Boolean.cshtml under Views / Shared / EditorTemplates in your project and you'll see a more friendly widget by default.

@model bool?
@using System.Web.Mvc

    var selectList = new List<SelectListItem>();
    selectList.Add(new SelectListItem { Text = "", Value = "" });
    selectList.Add(new SelectListItem { Text = "Yes", Value = "true", Selected = Model.HasValue && Model.Value });
    selectList.Add(new SelectListItem { Text = "No", Value = "false", Selected = Model.HasValue && !Model.Value });

@Html.DropDownListFor(model => model, selectList)

Now you can customise it as you please.

If you don't want to override the standard template, bear in mind that you can save it with a different filename e.g. FriendlyBool.cshtml and then call it explicitly like so:

@Html.EditorFor(model => model.myNullableBool, "FriendlyBool")

28 May 2012

jquery - dynamic load of html fragments with script

Sometimes I want to load a fragment of HTML into the current page.

Often this is a blob of HTML that came in as part of a JSON object from AJAX. Also, I only might to extract part of the HTML that's returned - because for example sometimes the incoming fragment may be a whole partial view and I only want part of it.

( Note : this is similar to the jQuery $('#target').load('mypage.aspx #divToExtract') syntax )

Well, if you want to do it WITHOUT running script from the incoming fragment, do this:

                        var frag = $(jsonObj.HtmlFragment);
                        frag = frag.find('#divToExtract');
But if you do want to run any script in the fragment, do this:
                        var frag = $('<div />');
                        frag.html( jsonObj.HtmlFragment );
                        frag = frag.find('#divToExtract');

11 May 2012

Simple jQuery Navigation Menu

<script type="text/javascript">

    $(function () {
        $('.MyNavHolder .SubSection').hide();
        $('.MyNavHolder .Section').hover(function () { $(this).find('.SubSection').toggle('fast'); });


    .MyNavHolder {background-color: #f00; overflow:visible; height: 30px; padding: 0px; position: relative;  }
    .MyNavHolder .MyNav {  position: absolute; right: 0; }
    .MyNavHolder a { color: #fff; text-decoration: none; display: block; }
    .MyNavHolder ul { margin: 0; padding: 0; list-style-type: none;}
    .MyNavHolder .Section { float: left;  background-color: Olive; width: 200px; cursor: pointer; }
    .MyNavHolder .Section .Header { height: 30px; border-left: 1px solid black;  }
    .MyNavHolder .Section .Header div { padding: 5px;  }
    .MyNavHolder .SubSection li { background-color: Fuchsia; padding: 5px; }
    .MyNavHolder .SubSection li:hover { background-color: Blue; }

<div class="MyNavHolder">
    <ul class="MyNav">
        <li class="Section">
            <div class="Header">
                <div><a href="">About Us</a></div>
            <ul class="SubSection">
                <li><a href="">Contact Us</a></li>
                <li><a href="">History</a></li>
        <li class="Section">
            <div class="Header">
                <div><a href="">McCormack</a></div>
            <ul class="SubSection">
                <li><a href="">Account Settings</a></li>
                <li><a href="">Log Out</a></li>

4 May 2012

Entity Framework 4 Set Default SQL Values On Save Changes

EF doesn't use the default values that you may have specified in your SQL Table schema.

So stuff like "DateCreated" fields that have getutcdate() as their default SQL value won't get set automatically when a record is saved from Entity Framework.

To solve that particular problem, I use the following approach. I enclose two styles, one for use with EF DbContext (POCO / CodeFirst), and one with EF ObjectContext (the default used in EDMX generation).

Note that you put this code within a partial extension of your EF Context class i.e. public partial class MyContext : DbContext or public partial class MyContext : ObjectContext

public override int SaveChanges()

	ObjectContext ctx = ((IObjectContextAdapter)this).ObjectContext;

	List<ObjectStateEntry> objectStateEntryList = ctx.ObjectStateManager.GetObjectStateEntries(EntityState.Added | EntityState.Modified | EntityState.Deleted)

	foreach (ObjectStateEntry entry in objectStateEntryList)
		if (!entry.IsRelationship)
			switch (entry.State)
				case EntityState.Added:
					if (entry.Entity.HasProperty("DateCreated"))
						entry.Entity.GetType().GetProperty("DateCreated").SetValue(entry.Entity, DateTime.UtcNow, null);
					if (entry.Entity.HasProperty("DateModified"))
						entry.Entity.GetType().GetProperty("DateModified").SetValue(entry.Entity, DateTime.UtcNow, null);

				case EntityState.Modified:
					if (entry.Entity.HasProperty("DateModified"))
						entry.Entity.GetType().GetProperty("DateModified").SetValue(entry.Entity, DateTime.UtcNow, null);

	return base.SaveChanges();
public override int SaveChanges(SaveOptions options)

	foreach (var insert in this.ObjectStateManager.GetObjectStateEntries(System.Data.EntityState.Added))
		if (insert.Entity.HasProperty("DateCreated"))
			insert.Entity.GetType().GetProperty("DateCreated").SetValue(insert.Entity, DateTime.UtcNow, null);
		if (insert.Entity.HasProperty("DateModified"))
			insert.Entity.GetType().GetProperty("DateModified").SetValue(insert.Entity, DateTime.UtcNow, null);

	foreach (var update in this.ObjectStateManager.GetObjectStateEntries(System.Data.EntityState.Modified))
		if (update.Entity.HasProperty("DateModified"))
			update.Entity.GetType().GetProperty("DateModified").SetValue(update.Entity, DateTime.UtcNow, null);

	return base.SaveChanges(options);

13 Apr 2012

ASP.NET MVC 3 - Html.TextBoxFor not showing model value

I have a Controller that passes a Model to a View.
The Controller receives the Model in a postback and modifies one of the Model properties before passing it to the View.
The View uses the Html.HiddenFor HtmlHelper to include the modified Model property in a Form.

BUT!!!! The HTML shows that the View is not rendering the modified Model property value! Instead it's rendering whatever the property value was when it was POSTed.

In short: HtmlHelpers use the posted form collection values in preference to the explicitly set model value. To me that's counter-intuitive.

Anyway, the best workaround is to use hand-made <input /> tags instead of using the HtmlHelpers. Feels hacky but works.

This guy has more info.

12 Apr 2012

Free license for VMWare ESXi 5 / Hypervisor

I hate the 60 day license when the free unlimited license is available - when you know where it is!

Go here: https://www.vmware.com/tryvmware/index.php?p=free-esxi5&lp=default

Click the tab midway down the page called "License and Download". Create an account and they'll give you a license code.

Then go into vSphere Client Inventory, click on the host server that you wish to license, and select Configuration > Software > Licensed Features > Edit

Hope that helps someone.

29 Feb 2012

Get / Show Entity Framework (EF) Generated SQL

Before I forget AGAIN , when you have some linq from EF and you want to see the generated SQL, cast it to ObjectQuery and call ToTraceString().

20 Feb 2012

Using iTextSharp to print Avery Labels PDF with ASP.NET MVC and C#

Tasked with printing labels nicely on Avery L7992 label sheets (or any other type of labels really) from a web application, I did the usual messing around with HTML + CSS in different browsers before deciding to start again and try using PDF generation as a means of getting more control. After a few hours, a bunch of googling, finding tips here and there, I got it to work.

To produce a PDF file, your best bet is to use an existing library. iTextSharp is a powerful PDF generation library for .NET (it's a port of the Java iText library). One downfall is a paucity of good free documentation (although Mike was very helpful: http://www.mikesdotnetting.com).

So, with a reference to itextsharp.dll and a couple of usings in the code (iTextSharp.text and  iTextSharp.text.pdf), this is basically all you need to generate PDF label sheets from your MVC Controller:

public ActionResult LabelsPdf()
	// Open a new PDF document

	const int pageMargin = 5;
	const int pageRows = 5;
	const int pageCols = 2;
	var doc = new Document();
	doc.SetMargins(pageMargin, pageMargin, pageMargin, pageMargin);
	var memoryStream = new MemoryStream();

	var pdfWriter = PdfWriter.GetInstance(doc, memoryStream);

	// Create the Label table

	PdfPTable table = new PdfPTable(pageCols);
	table.WidthPercentage = 100f;
	table.DefaultCell.Border = 0;

	var baseFont = BaseFont.CreateFont(BaseFont.HELVETICA, BaseFont.CP1252, false);

	foreach (var Thing in YourCollectionOfThings)
		#region Label Construction

		PdfPCell cell = new PdfPCell();
		cell.Border = 0;
		cell.FixedHeight = (doc.PageSize.Height - (pageMargin * 2)) / pageRows;
		cell.VerticalAlignment = Element.ALIGN_MIDDLE;

		var contents = new Paragraph();
		contents.Alignment = Element.ALIGN_CENTER;

		contents.Add(new Chunk(string.Format("Thing #{0}\n", Thing.ThingId), new Font(baseFont, 11f, Font.BOLD)));
		contents.Add(new Chunk(string.Format("Thing Name: {0}\n", Thing.Name), new Font(baseFont, 8f)));




	// Close PDF document and send

	pdfWriter.CloseStream = false;
	memoryStream.Position = 0;
	return File(memoryStream, "application/pdf");

27 Jan 2012

Windows Server 2008 - MVC 3 - getting 404 errors from IIS 7

Well this one got us stumped for a long time this afternoon, and not even the mighty StackOverflow could help us!

We have an MVC 3 site that was running happily in dev, but when the client asked us to install it on their server (Windows 2008 SP2 (NOT R2), IIS 7.0), it wouldn't work; every View returned a 404.

We had just installed MVC 3 and ASP.NET 4, so we went though all the usual suspects:
  • Integrated pipeline
  • Permissions
  • runAllManagedModulesForAllRequests in system.webserver
No dice.

Eventually a random comment on some forum led us to the KB980368 Hotfix. That solved the problem!

VAG-COM / VCDS in Yorkshire for Audi A4 B7 Cabriolet

I wanted to change some settings on my A4 Cabriolet. The settings were:
  • Turn off selective locking (fob click only opens driver's door)
  • Turn off speed locking  (auto locking after 10mph)
  • Turn off seatbelt warning (module 17 penultimate code flip digit to 0) 
  • Enable telephone voice control (Electronics 2 - module 77 penultimate code flip digit from 0->2)  
To do this you need special software and a cable to the diagnostic port. The official VAS kit is crazy expensive, but the Ross-Tech VAG-COM/VCDS kit is £250. Still too much for me. I knew *what* to do from my research on the Audi forums, but I didn't have the kit.

So after an hour of waiting at Harrogate Audi in their swanky coffee area, they finally told me they had been unable to make the changes, and couldn't spare any more time looking at it. They also said not to believe everything you read on the Internet.


Well, just wanted to say that a good chap called Chris Dodgson from Auto Diagnostic Services drove his van round to my office today, plugged in his VCDS laptop and sorted all the changes in about 10 minutes.

It goes to show that Harrogate Audi is just a showroom, and their service department doesn't know how to do simple electronic changes on Audis. What a ridiculous situation.

18 Jan 2012

ValueInjecter - matching nullable to non-nullable values

Here's a little ValueInjecter plugin I wrote to handle ValueInjecter matching for properties where the name matches but one is nullable and the other is not.
public class NullableInjection : ConventionInjection
 protected override bool Match(ConventionInfo c)
  return c.SourceProp.Name == c.TargetProp.Name &&
      (c.SourceProp.Type == c.TargetProp.Type 
    || c.SourceProp.Type == Nullable.GetUnderlyingType(c.TargetProp.Type)
    || (Nullable.GetUnderlyingType(c.SourceProp.Type) == c.TargetProp.Type 
      && c.SourceProp.Value != null )

 protected override object SetValue(ConventionInfo c)
  return c.SourceProp.Value;

So I just use it like so:
If I helped you out today, you can buy me a beer below. Cheers!