0 Comments

I was working on the design of a small site where we have public display of information in one format and a set of administrative screens that allow authorised users to create and edit the information.

“I know”, I thought, “this is a perfect case for MVC Areas” – and boy was I wrong!

I should have realised that the problems had started when all the routing started playing up..

  1. Route names must be unique across *all* areas
  2. If there are controller names in common in different areas, all routes need to be updated to bind the namespace of the controller you want!

Yuck! – did the MVC team think about the open-closed principle?

Then I realised I’d made a category error – mis-applying the area concept. I was making the same sort of error that people make when implementing SOA, breaking down services according to functional granularity rather than business function.

So, instead of having two Product controllers, one in the main area, the other in the admin area, I should have one Product controller, still securing the appropriate methods, and still using routes to present some of the views under ~/admin/product and some under ~/product.

Areas should be reserved for coherent chunks of business functionality e.g. Sales, Marketing etc and I’m still not particularly happy that if I have a concept in common e.g. Customer, then the routes are going to clash, but I’m glad I realised my error before I’d built even more in the wrong direction.

0 Comments
  •   Posted in: 
  • MVC

I’ve been using the MvcContrib.TestHelper class for a while now to get rather nice fluent testing of the routes in my MVC applications.

A typical test class would look like this

public class CategoryRoutesFixture : RoutesFixture
{
    private const int categoryId = 34;

    private static string BaseUrl
    {
        get { return "~/admin/Category"; }
    }

    [Test]
    public void Index()
    {
        BaseUrl.ShouldMapTo<CategoryController>(x => x.Index());
    }

    [Test]
    public void Create()
    {
        (BaseUrl + "/create").ShouldMapTo<CategoryController>(x => x.Create());
    }

    [Test]
    public void Show()
    {
        (BaseUrl + "/" + categoryId).ShouldMapTo<CategoryController>(x => x.Show(categoryId));
    }

    [Test]
    public void Edit()
    {
        (BaseUrl + string.Format("/{0}/edit", categoryId)).ShouldMapTo<CategoryController>(x => x.Edit(categoryId));
    }
}

The base class just takes care of initialising the routes collection from the MvcApplication.

I’ve just had a need to use the new Areas functionality in MVC 2.0, and one interesting wrinkle (there are a few!) is that that the RegisterAllAreas functionality wasn’t built with testing particularly in mind as it seems to need a running instance of ASP.NET to function correctly. After trying a few things and wading through even more blog posts I finally found an answer on StackOverflow.

The TestHelper class is not area aware, so the workaround is to configure the RoutesCollection appropriately for the class you are testing, so now for my main area I have

protected override void InitializeRoutes(RouteCollection routes)
{
    MvcApplication.RegisterRoutes(routes);
}

Whereas in my admin area I have

protected override void InitializeRoutes(RouteCollection routes)
{
    RouteTable.Routes.Clear();
    var adminReg = new AdminAreaRegistration();
    adminReg.RegisterArea(new AreaRegistrationContext(adminReg.AreaName, RouteTable.Routes));
}

Not particularly tricky once you know about it, but a pain to discover if you don't

0 Comments

I was chatting to a colleague recently who although he was a very good programmer, did not have a computer science/maths background – this was fine until he wanted to use bubble sort on a large(ish) (100k) number of records and I had to explain to him about how algorithms that take n^2 time are not your friend for big (or even little) n.

One of the most important decisions you can make when optimizing an application is the up-front choice of algorithm to use, performance tuning and optimization can make a difference but you are very lucky to achieve 2x-5x improvement, to be able to make 10x or 100x improvement in your application you need decent algorithm choice.

Here’s a little table I put together to help explain why you have to be careful and know the time/space performance of any algorithm you put in your applications.

Items

const

ln n

log n

n

n2

n3

2n

3n

n!

1

100111231

10

1

2

1

10

100

1,000

1,024

59049

3.63x106

100

1

5

2

100

104

106

1.268x1030

5.15x1047

9.33x10157

1,000

1

7

3

103

106

109

1.07x10301

 

 

10,000

1

9

4

104

108

1012

 

 

 

100,000

1

12

5

105

1010

1015

 

 

 

1,000,000

1

14

6

106

1012

1018

 

 

 

10,000,000

1

16

7

107

1014

1021

 

 

 

Now is this the approximate time/space growth you will get; how long your process will take will depend on the cost of each operation and to give you a guide, here’s now many units there are in one year

 SecondsMillisecondsMicroseconds
1 year3.16x1073.16x10103.16x1013

So if you happen to pick O(n3) algorithm you are going to have to waiting between 16 minutes and around 35 years to solve for just a 1,000 items!

Notice also that as the notation power grows, even micro-second execution times (or expanding onto Cloud infrastructure) isn’t going to help too much.

0 Comments

I find Visual Studio Item Templates very useful, but a lot of teams I encounter haven’t really adopted them as they can be a pain to construct. Here’s the technique I use to manage the templates and make it painless to add new ones and modify existing templates if they need to change. What we do is put the templates into source control and then use a build process to automate the production and distribution of the zip files.

There are two types of templates, project and item and they are accessed via directories configured under the Visual Studio options dialog

vstemplates

When working in a team, it is a good idea to map these two directories to a central location – that way as new templates are developed or improved the entire team benefits at once.

A template is a actually just a zip file containing one or more files plus a template file in XML that tell Visual Studio what to do – and that’s part of the problem. The directory structure in the zip must reflect exactly the structure where you want to put the files in the project and the templates themselves must be structured into a directory hierarchy to get the sections support in Visual Studio, for example see the screen shot of my MVP presentation tier templates.

vstemplates2

So, first of all create a root directory for all your templates, and then create two main subdirectories; items and projects. In each create subdirectories to classify your templates, e.g. Core, Data, Web, etc. In the appropriate directory you then need a directory per template, see below

vstemplates3

This is a simple template I have for creating a flag enum, i.e. an enum that is a bit field – the source is shown below.

namespace $rootnamespace$
{
    using System;

    /// <summary>
    /// 
    /// </summary>
    [Flags]
    public enum $filename$
    {
        // Suggest using hex rather than decimals as below to avoid overlapping/missing values
        A = 0x1,
        B = 0x2,
        C = 0x4,
        D = 0x8,
        E = 0x10,
        F = Ox20
    }
}

There are two macro replace values in the file, rootnamespace, which is replaced by the namespace of the directory you create the file in, and filename which is replaced by the value you enter in the Add Item dialog. We also have to produce the accompanying template file which must be called MyTemplate.vstemplate

<VSTemplate Version="2.0.0" xmlns="http://schemas.microsoft.com/developer/vstemplate/2005" Type="Item">
  <TemplateData>
    <DefaultName>Flags.cs</DefaultName>
    <Name>Flag Enum</Name>
    <Description>Enum that is meant to be a set of flags</Description>
    <ProjectType>CSharp</ProjectType>
    <SortOrder>10</SortOrder>
    <Icon Package="{FAE04EC1-301F-11d3-BF4B-00C04F79EFBC}" ID="4515" />
  </TemplateData>
  <TemplateContent>
    <References />
    <ProjectItem SubType="Code" TargetFileName="$fileinputname$.cs" ReplaceParameters="true">Enum.cs</ProjectItem>
  </TemplateContent>
</VSTemplate>

Now next we need the MSBuild Community Tasks assembly and an MSBuild project file, items.proj

<?xml version="1.0" encoding="UTF-8"?>
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" >
	<!-- Basic properties -->
	<PropertyGroup>
		<ToolsPath Condition="'$(ToolsPath)' == ''">C:\Program Files</ToolsPath>
	</PropertyGroup>
	
	<!-- Import a bunch of external targets -->
	<Import Project="$(MSBuildExtensionsPath)\MSBuildCommunityTasks\MSBuild.Community.Tasks.Targets"/>

	<!-- The templates we want to produce -->
	<ItemGroup>
		...
		<ItemTemplate Include="FlagEnum" />
		...
		<ItemTemplate Include="ViewBundle">
			<Category>Web\</Category>
		</ItemTemplate>
		...
	</ItemGroup>

	<!-- Ok, standard action is to create clean zip files -->
	<Target Name="Build" DependsOnTargets="Clean;Zip;" />

	<Target Name="Clean">
		<!-- Need this for the first time through when Build doesn't exist -->
		<MakeDir Directories="Build" Condition="!Exists('Build')" />
		<!-- Can't delete it if it has contents, so delete them first -->
		<Delete Include="Build\**\*.*" Condition="!Exists('Build')" />
		<RemoveDir Directories="Build" />
		<MakeDir Directories="Build" Condition="!Exists('Build')" />
	</Target>

	<Target Name="Zip" Outputs="%(ItemTemplate.Identity)" >
		<MakeDir Directories="Build\%(ItemTemplate.Category)" Condition="%(ItemTemplate.Category) != ''" />
		<CreateItem Include="$(MSBuildProjectDirectory)\%(ItemTemplate.Category)%(ItemTemplate.Identity)\**\*.*" 
			Exclude="$(MSBuildProjectDirectory)\%(ItemTemplate.Category)%(ItemTemplate.Identity)\**\_svn\**\*.*;$(MSBuildProjectDirectory)\%(ItemTemplate.Category)%(ItemTemplate.Identity)\**\.svn\**\*.*">
			<Output TaskParameter="Include" ItemName="ZipFiles" />
		</CreateItem>
		<Zip Files="@(ZipFiles)" 
			ZipFileName="$(MSBuildProjectDirectory)\Build\%(ItemTemplate.Category)%(ItemTemplate.Identity).zip" 
			WorkingDirectory="$(MSBuildProjectDirectory)\%(ItemTemplate.Category)%(ItemTemplate.Identity)" />
	</Target>
</Project>

I suggest keeping the template files in alphabetic order in the target list irrespective of directory, it’s much easier to work out if you have missed one out that way.

Now if you set up a project in your favourite continuous integration server, whenever any developer creates or improves a template, the build process produces all the zip files and then can copy them to the shared directory.

0 Comments

Sometimes doing very basic things can be quite tricky in new technologies; case in point presenting a DropDownList (a ComboBox to those, including me, with a VB background) using the new EditorForModel syntax in ASP.NET MVC 2.

I had a little class in my MVP framework called IdLabel which as the name implied just held an Id and a Label so that I wasn’t forced to have a separate display and edit model for every trivial case, so I wanted to do the same sort of thing when writing my shiny new MVC apps. There’s a class called SelectListItem which fits the bill, but unfortunately a decision late in the MVC 2 Beta meant that by default, the object display/edit templates will not process anything other than basic types.

There is a slight hacky workaround which is to implement your own TypeConverter to tell the framework that it can be turned into a string, but this is normally then coupled by using an Attribute at compile time – and we don’t own SelectListItem. Fortunately, there’s a way around this using a little bit of reflection

public static class TypeUtility
{
    /// <summary>
    /// Registers a type converter against a type we don't own
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <typeparam name="TConverter"></typeparam>
    public static void RegisterTypeConverter()
        where TConverter : TypeConverter
    {
        var attr = new Attribute[1];

        var vConv = new TypeConverterAttribute(typeof(TConverter));
        attr[0] = vConv;
        TypeDescriptor.AddAttributes(typeof(T), attr);
    }
}

So now starting with our very simple domain model

public class Supplier
{
    public int Id { get; set; }
    public string Name { get; set; }
}

public class Product
{
    public int Id { get; set; }
    public string Name { get; set; }
    public Supplier Supplier { get; set; }
}

we can have an equally simple view model

public class ProductViewModel
{
    public int Id { get; set; }
    public string Name { get; set; }
    [DropDownList]
    public SelectListItem Supplier { get; set; }
}
Now the custom DropDownList attribute and associated classes are taken from Kazi's blog with the slight modification of adding a couple of constructor overloads so that the defaults work for SelectListItem.

Having done all that we can proceed to what the controller has to do, what’s shown below is a simplification of my current controller which I'll tell you about shortly.

public ProductController 
{
     ...
     public ActionResult Edit(int id)
     {
          var entity = repository.FindOne(id);
          var model = builder.Convert(entity);
          
          var types = repository.FindAll();
          ViewData["SupplierList"] = builder.Convert(types);

          return VIew(model);
     }
     ...
}

The builder abstracts all of the work mapping the domain and view models and is based on AutoMapper.

Then we come to the display template, idea here is to show the label rather than the idea – one extension I have in mind is to convert this to a URL to allow display/edit of the related data.

<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl" %>
<script runat="server">
    string FormattedModelValue
    {
        get
        {
            // TODO: Might want to test for DropDownList attribute for other complex models.
            if (Model == null)
            {
                return null;
            }
            return Model is SelectListItem ? ((SelectListItem) Model).Text : Model.ToString();
        }        
    }
</script>
<%= Html.Encode(FormattedModelValue) %>

The next bit is the edit template. I’m using majic strings to couple the list definition of the DropDownList to avoid having to make the views strongly-typed on SelectLists; I view this as outside of the scope of the view model, though you may disagree. If there’s a need to alter this, the first parameter of the DropDownList attribute takes the ViewData key name.

<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl" %>
<%@ Import Namespace="Hippo.Web.Mvc.Templating" %>
<script runat="server">
    DropDownListAttribute GetDropDownListAttribute()
    {
        var metaData = ViewData.ModelMetadata as FieldTemplateMetadata;

        return (metaData != null) ? metaData.Attributes.OfType<DropDownListAttribute>().SingleOrDefault() : null;
    }
    
    IEnumerable<SelectListItem> GetSelectList()
    {
        var metaData = ViewData.ModelMetadata as FieldTemplateMetadata;        
        if (metaData == null)
        {
            return null;
        }

        var attribute = metaData.Attributes.OfType<DropDownListAttribute>().SingleOrDefault();
        if (attribute == null)
        {
            return null;
        }

        var key = attribute.ViewDataKey ?? (metaData.PropertyName + "List");
        var selected = attribute.GetSelectedValue(Model);
        // This makes it work for both SelectList and any other enumerable
        var sl = new SelectList(ViewData[key] as IEnumerable, attribute.DataValueField, attribute.DataTextField, selected);

        return sl;
    }
</script>
<% var attribute = GetDropDownListAttribute();%>
<% if (attribute != null) {%>
    <%= Html.DropDownList(null, GetSelectList(), attribute.OptionLabel, attribute.HtmlAttributes) %>
<% }%>
<% else {%>
    <%= Html.DisplayForModel()%>
<% }%>

The non-obvious problem here is that due to a bug in MVC 2, the Html.DropDownList builder ignores any selected item that you pass it and also does not look at the model (the offending line appears to be line 188 in SelectExtensions). The trick I found (today) was to push the Model value into the ViewData against the PropertyName, it does pick that up and pushes it as the selected item – there won’t be anything else there as we have a strongly-typed view, apart from the SelectLists.

One other thing to note is that Kazi’s DropDownList attribute can be given a list of anything and you can specify the Value and Text properties to be pushed into the SelectList. Also, your model doesn’t have to use SelectListItem, but if you don’t be prepared to write a TypeConverter each time you want a DropDownList!