Generating better default DisplayNames from Models in ASP.NET MVC using ModelMetadataProvider

Using the scaffolding in MVC makes it easy to knock up CRUD applications without too much difficulty. Typically you end up with something like this – note the PascalCase field names taken straight from the model generated by Html.LabelFor:

camelcase before

So now the obvious thing is to add DisplayName attributes metadata to your model, for example:

[DisplayName("Assigned To")]
public int AssignedToId { get; set; }

[DisplayName("Customer Name")]
public string CustomerName { get; set; }

Meh, donkey work. In most cases it just needs better default labels in the absence of explicit DisplayName annotations. Nine times out of ten field names such as AssignedToId and CustomerName would be simply expanded to Assigned To and Customer Name. (Of course, they don’t do this out of the box because these simple rules wouldn’t hold true for all languages.)

In MVC you can hook into the metadata discovery/generation process by implementing a ModelMetadataProvider, the default one of which is the DataAnnotationsModelMetadataProvider. So all I did was inherit from the latter and override the GetMetadataForProperty method and if no DisplayName has been specified on the model, create one based on the model’s camel-cased property name.

class MyFabulousModelMetadataProvider : DataAnnotationsModelMetadataProvider
   // Uppercase followed by lowercase but not on existing word boundary (eg. the start)
   Regex _camelCaseRegex = new Regex(@"Bp{Lu}p{Ll}", RegexOptions.Compiled);

   // Creates a nice DisplayName from the model’s property name if one hasn't been specified
   protected override ModelMetadata GetMetadataForProperty(
      Func<object> modelAccessor,
      Type containerType,
      PropertyDescriptor propertyDescriptor)
      ModelMetadata metadata = base.GetMetadataForProperty(modelAccessor, containerType, propertyDescriptor);

      if (metadata.DisplayName == null)
         metadata.DisplayName = displayNameFromCamelCase(metadata.GetDisplayName());

      return metadata;

   string displayNameFromCamelCase(string name)
      name = _camelCaseRegex.Replace(name, " $0");
      if (name.EndsWith(" Id"))
          name = name.Substring(0, name.Length - 3);
      return name;

Hook the provider up in Application_Start by assigning an instance of it to ModelMetadataProviders.Current.

It’s pretty effective I think:

camelcase after

Anything that isn’t quite right can be overridden simply my explicitly adding DisplayNames to the model.

Is SQL Server Profiler showing Connection Pooling not working?

TL;DR: No – it’s just SQL Profiler not telling you the entire truth.

In evaluating Entity Framework 4.1 (aka EF Code-First or “Magic Unicorn” Edition) I’ve been keeping an eye on what SQL it’s actually executing against SQL Server (how’s that for a leaky abstraction?). Here I saw something slightly worrying which was that it appeared that the client was logging in, executing a query and then logging out as seemingly indicated by the Audit Login and Audit Logout events:


The same thing happens without EF using basic SqlCommand queries. To tell you the truth I’d noticed this a while ago but hadn’t got round to investigating.

Rather than assume that connection pooling was broken on my machine, I had a hunch that SQL Profiler was somewhat misrepresenting what was really going on.

Indeed Markus Erickson on StackOverflow mentions the EventSubClass column you can add to SQL Profiler’s output to see if those Audit Logon/Logout events are actually connections being pulled from the pool or fresh connections.

Here’s how you show the EventSubClass column in SQL Profiler (I’m running SQL 2005 on this machine, I can only assume it’s similar on 2008):

  • Go to the Trace Properties window and switch to the Events Selection tab.
  • Click on the Show all columns checkbox
  • Scroll to the right and locate the EventSubClass column and check both checkboxes:

  • Then go to Organize Columns and move the EventSubClass column up so that it’s next to EventClass:


Now you can re-run your trace and hopefully be reassured that connection pooling after all is functioning correctly!


Hope that helps!

Running IISExpress without a console window

I created a little Windows Script file that you can put in the root of a site that when double-clicked will run IIS Express without its usual accompanying console window.

IIS Express seems to require a parent process so you have to keep the calling process alive whilst it’s running. The lowest tech way I could think of doing this is to use Windows Script Host’s WScript.Run method that allows you to spawn processes in a hidden window and wait for them to exit.

You can view and download the code here.

Just place the IISExpress.js in the root of your website and double-click, it should then launch your browser at the root of the site. You can adjust the port and CLR version by tweaking the variables at the top of the script.

Hope it helps!

Executing Cygwin Bash scripts on Windows

I was reading Jeremy Rothman-Shore’s post regarding kicking off a Cygwin script from a Windows batch file which addresses two things:

  1. Invoking Cygwin’s Bash, passing it a shell script to execute
  2. Resolving the script’s directory in bash so that it can access other resources its directory.

Here I want to improve a bit on the first point and come up with:

  • A general purpose “shim” that will execute a shell script on Windows in Cygwin
  • Will pass through any command-line arguments
  • Doesn’t trip up on any spaces in the path

The idea is that if I have a shell script called, I create a Windows equivalent called foo.cmd alongside it that can be called directly on Windows without the caller worrying about Cygwin etc (apart from having it installed):


(Or foo.bat – I prefer the *.cmd extension because this isn’t MS-DOS we’re running here).

The CMD script has to:

  1. Find its fully-qualified current location and locate its *.sh counterpart
  2. Translate this Windows-style path into a Unix style path
  3. Pass this to Cygwin’s bash along with any other arguments

Firstly, finding a batch file’s location is similar to how it’s done in Unix: it’s the first argument to the script. So in our case we use %0, and we can extract parts of the path like so: %~dp0 will extract the drive and path from argument 0 (i.e. the directory). See the for command for more information on this funky %~ syntax.

Secondly, the translation from a c:windowsstylepath to a /unix/style/path is done by Cygwin’s cygpath command. We do this in a slightly roundabout way via the ever versatile for command.

Thirdly, arguments passed to a batch file can either be accessed individually using $1, %2, $3 etc, or all in one go using %*, which is what we use here.

In addition, so that we don’t litter the Cygwin environment with temporary variables that we’ve created, we use a little setlocal/endlocal trick.

Here it is:

@echo off

if not exist "" echo Script "" not found & exit 2

set _CYGBIN=C:cygwinbin
if not exist "%_CYGBIN%" echo Couldn't find Cygwin at "%_CYGBIN%" & exit 3

:: Resolve to /cygdrive based *nix path and store in %_CYGSCRIPT%
for /f "delims=" %%A in ('%_CYGBIN%cygpath.exe ""') do set _CYGSCRIPT=%%A

:: Throw away temporary env vars and invoke script, passing any args that were passed to us
endlocal & %_CYGBIN%bash --login "%_CYGSCRIPT%" %*

Note that you just name this the same as your shell script with a .cmd or .bat file extension. and then just execute it as normal.

For example for the following shell script called

echo "Hello from bash script $0"
echo "Working dir is $(pwd)"
echo "arg 1 = $1"
echo "arg 2 = $2"

Here’s me calling it from Windows:


Hope that helps!

SOLVED: Windows Identity Foundation – “The system cannot find the file specified”

I’ve been working on a proof of concept for using claims-based authorisation with Windows Identity Foundation (WIF) against an Active Directory Federation Services (ADFS) 2.0 security token service (STS).

I seemed to have everything in place but came up against the following error in a yellow screen of death:

System.Security.Cryptography.CryptographicException: The system cannot find the file specified.


Looking at the stack trace it seems that Data Protection API (DPAPI which in .NET is exposed as System.Security.Cryptography.ProtectedData) is being used to encrypt data. A common use of DPAPI is to do encryption without you having to worry about key management: you leave it to Windows to worry about where the keys are stored. Those keys are typically buried in your user profile/registry somewhere – so it seemed odd DPAPI was being used here at all – the DPAPI keys would need to be part of the App Pool user account profile/registry.

Anyway, to cut a long story short I wasn’t Reading The Fine error Message fully. The interesting/useful bit was scrolled horizontally off-screen:

[CryptographicException: The system cannot find the file specified.]
System.Security.Cryptography.ProtectedData.Protect(Byte[] userData, Byte[] optionalEntropy, DataProtectionScope scope) +681

Microsoft.IdentityModel.Web.ProtectedDataCookieTransform.Encode(Byte[] value) +121
[InvalidOperationException: ID1074: A CryptographicException occurred when attempting to encrypt the cookie using the ProtectedData API (see inner exception for details). If you are using IIS 7.5, this could be due to the loadUserProfile setting on the Application Pool being set to false. ]
Microsoft.IdentityModel.Web.ProtectedDataCookieTransform.Encode(Byte[] value) +1280740
Microsoft.IdentityModel.Tokens.SessionSecurityTokenHandler.ApplyTransforms(Byte[] cookie, Boolean outbound) +74

Sure enough WIF was using DPAPI to encrypt a token, but DPAPI was complaining it couldn’t get to the keys because there was no user profile for the App Pool identity, which in this case Environment.UserDomainName/UserName told me was “IIS APPPOOLDefaultAppPool” – and there was no such user profile directory under C:Users.

So sure enough in IIS, in the advanced settings for the App Pool, Load User Profile was false, and setting it to true creates and loads user profile (a “DefaultAppPool” profile directory appeared in C:Users), and the application worked:


Typically WIF tutorials use the ASP.NET Web Sites, which use Cassini for testing, which runs under the current user identity and therefore a user profile with its DPAPI keys will be loaded – which is why if you use the standard run throughs/demos you don’t come up against this issue. But if you run as a Web Application under “real” IIS, this is when you may hit this problem.

It’s also debatable whether the use of DPAPI here is at all sensible. In a web farm environment the DPAPI keys for the App Pool identities across servers will be different, so if you don’t have sticky sessions enabled on your load balancer you run the risk of such federated logins not working 100% of the time. This issue is mentioned by Matias Woloski in the Geneva forums.

Another case of bad defaults all round.

Fixing WebSVN “Unable to call svn command” error

WebSVN is a great PHP web-based interface to Subversion that we run on our IIS build server. But, after updating to a more recent build we started getting errors like the following when viewing diffs:

Unable to call svn command "svn --non-interactive --config-dir /tmp"

The source of the error is in the getFileContents function in svnlook.php, where it’s piping the output of svn cat to GNU Enscript (for syntax highlighting) and then parsing the output of that using sed. I guessed that maybe the regex that sed is using isn’t being escaped properly for the Windows command line and therefore failing.

But I noticed that all of this code was in an if($config->useEnscript) block. Just before that is an if($config->useGeshi) block that looks a lot simpler. Simpler because GeSHi is PHP-based syntax highlighter, rather than an external command whose output requires special parsing. So I went to our include/config.php and added the $config->useGeshi(); call (as detailed in the distconfig.php template config file), and it all started working happily again!

Unzipping/extracting MSI files

If, like me, you are constantly wanting to just extract the files from a Windows Installer MSI file quickly, then this is for you.

My ZIP utility of choice 7-Zip appears to support extracting MSI files but in fact extracts all the various weird and wonderful binary streams in the MSI rather than simply just the actual files. Thankfully I stumbled across a Windows Installer switch today after typing msiexec /? that does the job perfectly: the /a “administrative install” switch, e.g.:

msiexec /a foo.msi /qb TARGETDIR="C:TEMPFoo"

So, what I’ve done is packaged this up as a little registry tweak that conveniently lets you do this by simply right clicking a file like so:


Copy and paste the following into a *.reg file and double-click it:

Windows Registry Editor Version 5.00
[HKEY_CURRENT_USERSoftwareClassesMsi.PackageshellExtract MSIcommand]
@="msiexec.exe /a "%1" /qb TARGETDIR="%1 Extracted""

Hope that helps!

Disabling direct PDF viewing/opening from the web

This worries me (more information). It would appear that you could craft PDFs that could hijack someone’s machine simply by their opening the PDF. This is especially pernicious because Adobe Reader is so intent on integrating PDF into Internet Explorer using various Browser Helper Objects and ActiveX Controls that a “drive-by” hijack would be simple to set up once you’ve crafted the malicious PDF file.

A quick fix (that Works On My Machine™) that prevents the automatic opening/viewing of PDF files from the web is to change the HKEY_CLASSES_ROOT.pdfContent Type registry key from application/pdf to something like application/octet-stream. This means that you have to save the files first before opening them.

Some possible scriptable ways of automating this:

Method 1: A .REG file

Save the following into a file with a “.reg” file extension and running using regedit.exe /s filename.reg.

"Content Type"="application/octet-stream"

Method 2: The REG command

Alternatively the following, using the reg.exe command will achieve the same result:

reg add HKCR.pdf /v "Content Type" /d "application/octet-stream" /f

You obviously still need to be careful about PDFs, you just have the ability now to actively decide whether you want to open them.

Cisco home page FAIL

UPDATE: it’s over 2 hours later now and they’ve finally fixed it 🙂

Cisco’s home page this morning: looks like they ran out of their allocation of lowercase letter ‘t’ (thanks to Chris Venus for pointing it out):


Or was it “Speak Like an Irishman” day and nobody told me?

Lowercase ‘t’s were being stripped from the source which explains why there were no styles, etc:


Chris reckons it’s a possibly a whitespace-trimming RegEx gone wrong, WDYT?