Switching the client side build library in visual studio 2013 MVC template to gulp and bower

Why?

A lot of people use Mads Kristensen’s absolutely awesome Web Essentials plugin for Visual Studio – we use it for less compilation, and bundling of our less/js.  It does however fall down when you need to use it in a continuous integration context, so we find that we keep the compiled/bundled output in our repository.

Couple that with the fact that in the next release of visual studio, gulp/grunt/bower are becoming first class citizens in terms of it’s support out of the box.

Scott Hanselman’s point in that post is a valid one – nuget is a superb addition to the .net ecosystem, and compare it to the dark days of ‘download a DLL from somewhere and hope’, it’s revolutionised .net development.  But there are other, arguably far better, and certainly far richer ecosystems out there for client side build, which on the one hand is absolutely awesome (npm is easy to build for and publish modules to), and on the other hand, daunting (I counted at least 15 modules that would simply minify my css for me).  Thankfully, the community talks/blogs a lot about this, so finding commonly used packages is as easy as reading from a number of sources and seeing which one comes out on top.

Microsoft are to be applauded for taking this approach and opening up the pipeline in this way – their whole approach recently with OSS of the .net clr, as well as the potential promise of a reliable .net on linux via vNext, and it’s a great time to be a .net dev.

All code for this example post is available at https://github.com/terrybrown/node-npm-gulp-bower-visual-studio

What is Gulp?

I won’t go into detail, as many other posts cover it well.  Essentially, it is a streaming build system written in node that allows people to create tasks and build up a pipeline of activities such as transforming less, copying files, validating javascript, testing, etc.  It is a more recent addition to the market (grunt, a tool with similar aims, though a different approach is another in the same arena).

What is Bower?

Essentially, a package manager for front end libraries (be they javascript, css, etc.) – think of it at a rudimentary level like nuget for client libraries.  There is a very good short video on egghead.io

Holy wars solved early – Gulp vs Grunt

Clever people have written about this.  I personally prefer the streams approach and the code over configuration driven nature of gulp over the ‘temp file all the things’ and config based approach of grunt.

Getting Setup – local dev machine + visual studio

Machine needs to be running node and gulp (gulp needs to be installed globally)

Node has just hit v 0.12 which has a number of updates (not least to streams3 and away from the somewhat interesting streams2)

node --version

Will confirm which version of node you’re running.  You don’t need the latest version, though the update in 0.12 has been a long time coming.

Setting up gulp/bower

npm install gulp -g
gulp --version
npm install bower -g
bower --version

TRX – Task Runner Explorer: This will give you a custom task runner for gulp within visual studio.

NPM/NBower Package Intellisense: Who doesn’t like intellisense right?

Grunt Launcher: Not ideally named, but a great little add on to give you right click support for gulp/bower and grunt.

You may also want to follow the steps in http://madskristensen.net/post/grunt-and-gulp-intellisense-in-visual-studio-2013 to get full intellisense.

Note: Switch off build in web essentials (it’s being used purely for intellisense)

File > New Project – and a tidy up

We want to hand over all JS and CSS handling to gulp.  This includes bundling and minification, as well as hinting/linting. We’ll start with the default MVC template from Visual Studio as the basis of our work.

Remove asp.net bundling/optimization

In the current template for MVC sites, Microsoft provide a handy bundling mechanism that although fine for smaller sites, still maintains the same problems as above and doesn’t give you separate control over your ‘distribution’ JS/CSS.  We’ll remove:

Microsoft.AspNet.Web.Optimization (and dependencies WebGrease, Antlr, Newtonsoft.Json)

This will also involve a few changes to web.config and the codebase (see https://github.com/terrybrown/node-npm-gulp-bower-visual-studio/commit/5cfb58b8e57faa4c518a067fa473d740e43725a3)

Remove client side libraries (we’ll replace these later)

  • bootstrap 3 (bower: bootstrap)
  • jquery (bower: jquery)
  • jquery validation (bower: jquery-validation)
  • jquery unobtrusive validation (bower: jquery-validation-unobtrusive)
  • modernizr (bower: modernizr)
  • RespondJS (bower: responsd)

Setting up Bower

bower init

This will lead you through a number of questions (accept defaults throughout for now, though you can read up on the options here)

You will end up with a bower.json file that will look something like:

image

Re-installing javscript and css dependencies

Take all of the package references above that we removed (the bower versions) and run the following on the command line:

bower install bootstrap jquery jquery-validation jquery-validation-unobtrusive modernizr respond --save

Do NOT forget the ‘- -save’ postfix at the end – this will ensure that your bower.json is updated with the local dependencies.

This will start the download and install, and you will end up with a new folder in your solution called ‘bower_components’ folder which contains all of the local dependencies.  Ensure you add this folder to your .gitignore (or source control ignore file of choice).

As a temporary step, switch to visual studio – add the ‘bower_components’ folder to your solution, and re-map all of your js/css files from the default template to the newly downloaded versions.

image

Setting up the build with Gulp

Firstly, we need to get this local solution ready to receive npm packages as dependencies (gulp + the other supplemental libraries we’ll be using are available via npm.

npm init

Again, accept all of the defaults really, or whatever you fancy in each field.

The examples from here down will be somewhat contrived – your own use case can dictate what you do at each step here, but for the purposes of example, what we want to achieve is:

  • Deliver all jquery and jquery validation libraries into a single request
  • Deliver bootstrap and respond as a single request
  • Create a basic more modularised structure for our CSS using less and then concatting/minifying as part of the build

In our real use cases at work, our needs are far more complex, but the above will serve as an example for this post.

Setting up a default ‘gulpfile.js’.

var gulp = require('gulp');

// define tasks here
gulp.task('default', function(){
  // run tasks here
  // set up watch handlers here
});

You can name and chain tasks in gulp really easily – each one can act independently or as part of an overall build process, and TIMTOWTDI (always) – what I’ll put forward here is the version that felt easiest to read/maintain/understand.

Deliver multiple vendor libraries into a single request

var gulp = require('gulp');
var del = require('del');
var concat = require('gulp-concat');

var outputLocation = 'dist';


gulp.task('clean', function () {
	del.sync([outputLocation + '/**']);
});

gulp.task('vendor-scripts', function () {
	var vendorSources = {
		jquery: ['bower_components/jquery/dist/jquery.min.js',
	'bower_components/jquery-validation/dist/jquery.validate.min.js',
	'bower_components/jquery-validation-unobtrusive/jquery.validate.unobtrusive.min.js']
	}

	gulp.src(vendorSources.jquery)
		.pipe(concat('jquery.bundle.min.js'))
		.pipe(gulp.dest(outputLocation + '/scripts/'));
});


gulp.task('default', ['clean', 'vendor-scripts'], function(){});

Ok, there are a number of things in here – key points:

  1. Read from the bottom up over – if you issue a straight ‘gulp’ command on the command line, you wil always run the ‘default’ task.  In this case, it doesn’t do anything itself (the empty function as the third param), but instead has a chained dependency – it’ll run ‘clean’ first, then (upon completion) run ‘vendor-scripts’ tasks.
  2. ‘clean’ task uses the ‘del’ npm module to clean out the output folder we will be pushing the built scripts/css to.
  3. ‘vendor-scripts’ uses the ‘gulp-concat’ npm module to simply join an array of files together (in this case, the jquery + jquery validation files)

if you switch to a command prompt window and run ‘gulp’ on it’s own, you will see output similar to:

image

And in visual studio, you will now see a hidden ‘dist’ folder there with the output of what you have just generated (remember to update your .gitignore – you do not want to commit these)

Disabling Web Essentials

Less has been our tool of choice for our CSS for some time now, and web essentials really did/does rock as a VS plugin to aid your workflow on those (nice inbuilt bundling, compilation, etc.  That said, now that we’re moving to a more customised build process, we need to switch the compilation side of it off.

Tools > Options > Web Essentials

Switch everything in ‘Javascript’ and ‘LESS” to false.

Deliver minified and concatenated CSS from LESS

We contrived a number of .less files in order to create the proof of concept:

_mixins.less

@brand_light_grey_color: #EFEFEF;

.border-radius(@radius: 4px) {
	-moz-border-radius: @radius;
	-webkit-border-radius: @radius;
	border-radius: @radius;
}

layout.less

@import "_mixins.less";

body {
    padding-top: 50px;
    padding-bottom: 20px;
}

/* Set padding to keep content from hitting the edges */
.body-content {
    padding-left: 15px;
    padding-right: 15px;
}


/* Override the default bootstrap behavior where horizontal description lists 
   will truncate terms that are too long to fit in the left column 
*/
.dl-horizontal dt {
    white-space: normal;
}

div.rounded {
	.border-radius(4px);
}

forms.less

@import "_mixins.less";

/* Set width on the form input elements since they're 100% wide by default */
input,
select,
textarea {
    max-width: 280px;
}

Nothing complex, though it’ll let us at least build a workflow around them.

There are a couple of key tasks we want to perform here:

  1. Grab all less files and compile them over to css
  2. Compress that css
  3. Push them all into a single file in our dist folder

Thankfully, the ‘gulp-less’ plugin performs the first two tasks, and we have already achieved the other for our JS so it’s just a repeat of those steps.

Integration into Visual Studio and tying it all together

We now have a basic working build that we can add to as and when our process demands – node and the node package manager (npm) have a massive ecosystem of libraries to support all sorts of tasks (generaily, gulp- prefixed for gulp related build tasks), so you can start to build from this point forward.

Key thing now is tying this workflow into Visual Studio, and this is where the cool happens.  The Task Runner Explorer gives us a lot of extensibility points.

image

Each of these tasks/sub-tasks can be right clicked and ran as you would do from the command line easily, but you also have a nice option to ‘bind’ certain actions in Visual Studio to steps within your grunt build.

E.g.

image

In this instance, we have bound our ‘clean’ gulp task to a ‘clean solution’ within visual studio.

Tying it all together – watching the solution

Web essentials was awesome at monitoring your work real time and updating bundled files (both less and js) into their respective outputs, but thankfully, gulp comes to the rescue in the guise of ‘gulp-watch’ – this is a highly configurable module that allows you to perform actions on changes to files.

Thankfully, now that we have all of the other tasks, the watch workflow is simply a matter of matching up targets to watch, and tasks to run when things happen to those targets.

var watch = require('gulp-watch');

gulp.task('watch', function () {
	gulp.watch('bower_comonents/**/*', ['vendor-scripts', 'vendor-css']);
	gulp.watch('Content/**/*.less', ['css']);
});

gulp.task('default', ['clean', 'vendor-scripts', 'vendor-css', 'css', 'watch'], function(){});

Once we have that, we can go back to the task runner explorer, right click the ‘watch’ task, and set it to run on solution open.

We now have our solution in watch mode permenantly and any changes to our less or the vendor scripts will trigger the appropriate tasks.

What’s next?

We’ve solved the problem (compiled css/js needing to be in our repo with web essentials), so the next steps really are incorporating this gulp build task into our CI server (TeamCity), though we’ll leave that for a follow up post.

Now that we have a whole set of automation going, we may as well re-introduce linting/hinting of our less and javascript too – some configuration will be needed here to ensure we’re happy with the outcomes, but fundamentally the ‘right thing to do’.

Testing our JS workflow is the next natural step, and there are plenty of gulp+other task runners to sit within this workflow that will let you validate your scripts either at build time or at save.

Creating a drop down list from an enum in ASP.NET MVC

Thought I’d share some work we’ve done in our MVC projects to ease the generation of drop down lists from enum types which makes life a hell of a lot easier for us when working with enums in views.

The basic premise focuses around the method below which is represented all over the web really (a lot of people seem to have come up with the solution at around the same time it seems) which is given an enum:

public enum UserType
{
	Visitor = 1,
	NonDepositor,
	DepositedOnce,
	DepositedTwice,
	Regular,
	LapsedRegular,
	LapsedNonDepositor
}

We can create a simple enum to select list convertor with the following:

public static SelectList ToSelectList<TEnum>(this TEnum enumObj)
{
	var values = (from TEnum e in Enum.GetValues(typeof(TEnum))
					select new { ID = e, Name = e.ToString() }).ToList();

	return new SelectList(values, "Id", "Name", enumObj);
}

Caveat: I didn’t invent this, it’s a pattern that’s published in a lot of places (stack overflow and other peoples blogs).

Making it look pretty

This may well work fine for a lot of your use cases or indeed for simple admin/internal systems, but our use cases dictated we extend this a little.  First and foremost was getting friendly strings out of this for the display value (our users like Words Separated With Spaces – curious that).

You could easily go with a simple regex on the ‘ToString()’ part of that code – something like:

public static string PascalCaseToPrettyString(this string s)
{
	return Regex.Replace(s, @"(\B[A-Z]|[0-9]+)", " $1");
}

And your call in the ‘ToSelectList’ method above would just be ‘ToString().PascalCaseToPrettyString()’ (for info: the regex above will take all uppercase characters or collections of numbers that aren’t at a word boundary and put a space in front of them).  This would give us something like ‘Deposited Once’ as opposed to ‘DepositedOnce’

Again, this may well suit exactly what you want, but what if the description you want to show to the user really doesn’t match what you want as the enum value.  For this, we look to the [Description] attribute and would decorate up our enum as follows:

public enum UserType
{
	[Description("Visitor (Not logged in)")]
	Visitor = 1,
	[Description("Non-depositing player (Created account, no deposits)")]
	NonDepositor,
	[Description("Single depositing player")]
	DepositedOnce,
	[Description("Twice depositing player")]
	DepositedTwice,
	[Description("Regular depositing player (Has 3 or more deposits)")]
	Regular,
	[Description("Lapsed Regular (Not logged in for the past 12 weeks)")]
	LapsedRegular,
	[Description("Lapsed Non-Depositor (Not deposited, not logged in for the past 12 weeks)")]
	LapsedNonDepositor
}

In this case we can simply extend our ‘PascalCaseToPrettyString’ concept a little further with:

public static string GetDescriptionString(this Enum val)
{
	try
	{
		var attributes = (DescriptionAttribute[])val.GetType().GetField(val.ToString()).GetCustomAttributes(typeof(DescriptionAttribute), false);

		return attributes.Length > 0 ? attributes[0].Description : val.ToString().PascalCaseToPrettyString();
	}
	catch (Exception)
	{
		return val.ToString().PascalCaseToPrettyString();
	}
}

This will attempt to grab the DescriptionAttribute from the enum value if there is one.  This will handle both situations (with and without Description attribute) nicely, and falls back to at least something that looks nice to the user if a description attribute isn’t present).  Our ‘ToSelectList()’ method will then just update to call .GetDescriptionString()’ instead of ‘ToString()’ for the value  (you will have to change the enum call like so):

public static SelectList ToSelectList<TEnum>(this TEnum enumObj)
		{
	var values = (from TEnum e in Enum.GetValues(typeof(TEnum))
					select new { ID = e, Name = (e as Enum).GetDescriptionString() }).ToList();

	return new SelectList(values, "Id", "Name", enumObj);
}

And we’re left with:

image

So far so good – what next?

The next steps are really edge cases, though it was useful to extend the helper in our use cases to deliver flexibility in all cases where we needed it.

Filtering

There are situations where you want to include only those options that are applicable based upon some other selection parameter or indeed some particular use case.  For this we can use a Func delegate along the lines of:

public static SelectList ToSelectList(this TEnum enumObj, Func predicate = null)
{
	IEnumerable values = (from TEnum e in Enum.GetValues(typeof(TEnum))
									select e);

	if (predicate != null)
		values = (from TEnum e in values
					where predicate(e)
					select e);

	var outputs = (from TEnum e in values
					select new { ID = e, Name = (e as Enum).GetDescriptionString() });

	return new SelectList(outputs, "Id", "Name", enumObj);
}

And in our views we can do something along the lines of:

<p>@Html.DropDownListFor(model => model.BankBalanceState, Model.BankBalanceState.ToSelectList( x => x != UserType.LapsedNonDepositor &&
				                                                                                    x != UserType.LapsedRegular))</p>

Adding ‘Please select’ as the first option

A simple one, though it saves you from having to jump through a few hoops if it’s important to have the ‘please select’ option at the top of the list.  This one requires a little more change to our helper method:

public static SelectList ToSelectList(this TEnum enumObj, Func predicate = null, bool addPleaseSelect = false)
{
	IEnumerable values = (from TEnum e in Enum.GetValues(typeof(TEnum))
									select e);

	if (predicate != null)
		values = (from TEnum e in values
					where predicate(e)
					select e);

	var outputs = (from TEnum e in values
					select new SelectListItem { Value = e.ToString(), Text = (e as Enum).GetDescriptionString() });

	if (addPleaseSelect)
	{
		var pleaseSelect = new List { new SelectListItem { Text = "--- please select ---", Value = "" } };
		outputs = pleaseSelect.Concat(outputs).ToList();
	}

	return new SelectList(outputs, "Value", "Text", enumObj);
}

Which leaves us with:

image

Shuffling the values

Another edge case though one that was useful to us in a number of situations was the shuffling of the values within the list.  We achieved this using a simple extension method:

public static ICollection ShuffleList(this ICollection list)
{
	return list.OrderBy( x => Guid.NewGuid()).ToList();
}

And included it in the updated ToSelectList like so:

public static SelectList ToSelectList(this TEnum enumObj, Func predicate = null, bool addPleaseSelect = false, bool shuffleList = false)
{
	IEnumerable values = (from TEnum e in Enum.GetValues(typeof(TEnum))
									select e);

	if (predicate != null)
		values = (from TEnum e in values
					where predicate(e)
					select e);

	if (shuffleList)
		values = values.ToList().ShuffleList();

	var outputs = (from TEnum e in values
					select new SelectListItem { Value = e.ToString(), Text = (e as Enum).GetDescriptionString() });

	if (addPleaseSelect)
	{
		var pleaseSelect = new List { new SelectListItem { Text = "--- please select ---", Value = "" } };
		outputs = pleaseSelect.Concat(outputs).ToList();
	}

	return new SelectList(outputs, "Value", "Text", enumObj);
}

Which is called from the view like so:

<p>@Html.DropDownListFor(model => model.BankBalanceState, Model.BankBalanceState.ToSelectList(shuffleList: true))</p>

Other extensions to this?

We’ve come up with a few more updates to this – one to force presentation via the enum numeric value (oddly in an enum, -1 is rendered after 1 and this isn’t always what you’d hope for).  We’ve also updated it for our multi-tenant websites to support localisation of enum values (though there’s enough work in this to provide an entirely separate blog post).  We’ve also added an optional parameter to ignore the current value of the enum (default to the first value in the select list rather than the selected enum) – again, an edge case, though I’m sure folks can see use cases themselves for this.

Hopefully that was useful – had been meaning to write it up for a while now (we’ve been using it in production now for over a year and it performs quite happily and there seem to be no bottlenecks/issues with it).

Grab the code

I’ve put the finished solution onto github if anyone wants to grab it and modify it themselves.  If anyone has suggestions on improvements feel free to send a pull request.

Localisation of your ASP.NET MVC 3 Routes

Our core product has recently undergone a localisation exercise as we plan to launch it in other european countries.  One of the first things we needed was to localise the routes on a per-country basis.

We started out remarkably luckily in that every route we delivered in the app was already custom.  We didn’t like the default route handler (Controller/Action/{stuff}) URL structure, and although we could have gone down the custom route handler approach, there were a few things that steered us away from that.

  1. we wanted full flexibility from an SEO point of view – as we dev’d we had no idea what would work well from an SEO point of view, so having each potential route customisable to whatever our SEO company desired was going to be a bonus.
  2. longer term plans will see us delivering a content management system to deliver an awful lot of the content – at that point, we may well be delivering custom routes via the DB too, so having a flexible routing system was essential.

Why not the default routes?

An example of some of the ‘out of the box’ routes we’d have gotten with the default route handler, versus what we actually wanted:

/MyAccount/UpdatePersonalDetails –> my-account/personal-details

/Winners/ByGame/{GameName} –> winners/by-game/{game-name}

Although generally, the conversion was a hyphen between caps and a full lowercasing, we found that replacing the default route handler with a custom ‘HyphenateAndLowercaseRouteHandler’ just didn’t answer enough of our use cases.

I’m sure google, bing and the other search engines will happily look at pascal cased words and discern them, though I as a human find it easier to read /our-new-game-has-paid-out-3-million-so far than /OurNewGameHasPaidOut3MillionSoFar.

One of the big selling points for not using the default routing was flexibility – we can change the routes without having to refactor/rename controllers or action methods, so there is a real separation there.

So, we started to build up our routing table with custom entries for each controller/action such as:

 routes.MapRoute("GameHistory", "game-history/{gameName}/{room}",
						new
						{
							controller = "BingoGamesHistory",
							action = "Index",
							gameName = "Bandit",
							room = "the-ship"
						}, namespaces);

and to date, across the whole front end application we have 183 custom routes.

Localising the Routes

It almost feels sham-like to be writing a blog post about this, though I still see questions on stack overflow about it, so thought I’d write this up.

What we did in the above example was replace the string (“game-history/{gameName}/{room}” with a localised resource – we now have a LocalRoutes which has something like the following:

image

and the routes.MapRoute command in global.asax replaces the string representation of the route with LocalisedRoutes.GameHistory_General.

Obviously from this point on, it’s then just a matter of adding a LocalisedRoutes.GameHistory.it, or LocalisedRoutes.GameHistory.es etc. to get the represnetation of the routes for those countries, and in our CI deployment the plan is to alter the web.config depending upon the deployment:

<globalization uiCulture="it-IT" culture="it-IT" />

Jobs a good un Smile

What next?

As I say, the next big phase of our project will include a content management system, so may well require us to have runtime routes injected into the routing table – I’ve never done it, but it’s something to be aware of. 

Sample Project

I’ve put together a simple project that demonstrates the above which will be something that folks can base their solutions upon if they they are having difficulty with the above description.  The example only localises routes, so the UI still remains in english, but you get the idea.

Download the example at google code

Unit Testing with DataAnnotations outside of MVC

This past week has seen us start on a big project at work to re-architect the site into .net and MVC2.  Naturally we have our models in a separate project, and we have two separate test projects (Unit and Integration) setup to use NUnit.

As it’s early days for us, and our first “real” MVC project I thought I’d write this up, a) as an aid to learning for me, but b) to try to gain feedback from the community on what they do with regards validation on their models.

I can see a few different ways we could have done this (annotate the ViewModels we’ll use on the front end, build in logic into our setters to validate, etc. etc.) but we’re now going down a route that so far feels ok.  That said, we’re focussing solidly on the modelling of our business logic at present, so haven’t yet brought the model “out to play” as it were.

Hopefully the above gives a wee bit of insight into where we are with it.

We’ve decided to plump for the MetaData model approach to keep the main objects slightly cleaner – an example for us would be:

namespace MyCompany.Models.Entities
{
	/// 
	/// 
	/// 
	[MetadataType(typeof(MyCompanyUserMetaData))]
	public class MyCompanyUser
	{
		public int UserId { get; set; }

		public string Username { get; private set; }
		...

		public void SetUsername(string newUsername)
		{
			if (Username != null)
				throw new ArgumentException("You cannot update your username once set");

			//TODO: where do we ensure that a username doesn't already exist?
			Username = newUsername;
		}
	}
}

and then in a separate class:

namespace MyCompany.Models.Entities
{
	public class MyCompanyUserMetaData
	{
		[Required(ErrorMessage="Your password must be between 6 and 20 characters.")]
		[StringMinimumLength(6, ErrorMessage="Your password must be at least 6.")]
		public string Password { get; set; }

		[Required(ErrorMessage="Your username must be between 6 and 20 characters.")]
		[StringLength(20, MinimumLength=6, ErrorMessage="Your username must be between 6 and 20 characters.")]
		[MyCompanyUserUsernameDoesNotStartWithCM(ErrorMessage="You cannot use the prefix 'CM-' as part of your username")]
		[CaseInsensitiveRegularExpression(@"^[\w\-!_.]{1}[\w\-!_.\s]{4,18}[\w\-!_.]{1}$", ErrorMessage = "Your username must be between 6 and 20 characters and can only contain letters, numbers and - ! _ . punctuation characters")]
		public string Username {get;set;}
	}
}

With all of this in place you’re all well and good for the MVC world, though unit testing just doesn’t care about your Annotations so your simple unit tests:

		
[Test]
public void SetUsername_UsernameTooShort_ShouldThrowExceptionAndNotSetUsername()
{
	// Arrange
	testUser = new MyCompanyUser();
			
	// Act

	// Assert
	Assert.Throws(() => testUser.SetUsername("12345")); // length = 5
	Assert.That(testUser.Username, Is.Null, "Invalid Username: Username is not null");
}

won’t give you the expected results as the logic of that is based upon the DataAnnotation.

What was our solution?

After much reading around (there didn’t seem to be an awful lot out there covering this) we took a two step approach.  First was to allow SetUsername to validate against the DataAnnotations like so:

		
public void SetUsername(string newUsername)
{
	if (Username != null)
		throw new ArgumentException("You cannot update your username once set");

	Validator.ValidateProperty(newUsername, new ValidationContext(this, null, null) { MemberName = "Username" });

	//TODO: where do we ensure that a username doesn't already exist?
	Username = newUsername;
}

Validator is well documented and there are a few examples out there of people doing this within their setters.  Essentially validating the input for a particular MemberName (Username in this case).

The second step was necessary because of the approach we’d taken with the MetaData class above, and it was a mapping in the TestFixtureSetup within our unit tests:

TypeDescriptor.AddProviderTransparent(new AssociatedMetadataTypeTypeDescriptionProvider(typeof(MyCompanyUser), typeof(MyCompanyUserMetaData)), typeof(MyCompanyUser));

This line (though I’ve yet to look down at the source code level) would appear to just be a standard mapping for the class to tell it where to find the metadata/annotations.

After putting those two things in place, the unit tests successfully validate against the annotations as well as any coded business logic, so jobs a good un!

Was it the right solution?

This is where I ask you, the person daft enough to suffer this blog post!  I have no idea if there is a better way to do this or how this will pan out as we propagate up to the MVC level – will I be causing us headaches taking this approach, will it simply not work because of overlap between the way MVC model binder validates versus what we’ve done down at the domain level?

It’s still early days for the project, and the above feels like a nice way to validate down at a business domain level, but how it pans out as we propagate wider and start letting other projects consume, hydrate and update the models… well that’s anyone’s guess!

Comments very much welcome on this one folks 🙂

NerdDinner, and initial thoughts on MVC

Although I’ve not yet finished it, I thought I’d start my wee reflection on MVC as learned through NerdDinner.

Obviously, the immediate thing that hits you is that you aint in Kansas any more – ignore the asp.net postback model, it’s all change and there is going to be some significant re-learn before I get anywhere near good I think.

I do love the separation of concerns, the theory behind it is sound from a maintenance and extensibility point of view.  Keeping my model tucked away nicely, and using it to provide the meat that the controller feeds of, which then in turn drives the View I think makes perfect sense.  I need to work far more heavily on the URL Routing before starting to design anything bigger just to see how a richer navigation hierarchy will sit.

I love the way postbacks are handled (at least in the NerdDinner app) and AcceptVerbs() just makes sense to me.  I can see I’m going to have to read up a bit more on programming against an interface, as I haven’t covered so much of this.  I wasn’t a big fan of the Respository pattern, I’d have perhaps gone down the facade route, or (when and if I understand it) perhaps IoC will help with this, though obviously this was just one example.

It’s my first successful play with Linq to SQL, and I’m liking the abstraction and the codified approach to it, though I’ll have to run some heavier tests through SQL Profiler to see how it works in terms of performance.

I’m going to have to look through the source code to find out just how all of the helper methods work rather than just use them – chucking Html.ActionLink() on the page is all well and good, but I want to know what it actually does in the code (easily enough done now that MVC source code is available)

I’m only just getting now to the integration/work with Ajax, which I think will be interesting – I shall keep the blog updated with stuff as I cover it.