Saturday, May 31, 2008

Interface + Extension Methods = Mixin

Mixins are a language idea similar to multiple inheritance, but rather than extending a class a mixin works by defining an interface plus various functionality associated with that interface. It avoids the pathologies of multiple inheritance and is a flexible way of providing add-on functionality.

In C# we can create a mixin with a combination of an interface plus extension methods. LINQ is the canonical example of this with two core interfaces IEnumerable<T> and IQueriable<T> and a collection of extension methods on those interfaces.

It's easy to create your own mixins. For example, say I want to provide location functionality to various entities. I can define an interface like this:

public interface ILocatable
{
   int Longitude { get; set; }
   int Latitude { get; set; }
}

And then some extension methods for that interface:

public static class LocatableExtensions
{
   public static void MoveNorth(this ILocatable locatable, int degrees)
   {
       // ...
   }

   public static void MoveWest(this ILocatable locatable, int degrees)
   {
       // ..
   }
}

Now, if we've got some entities like:

public class Ship : ILocatable
{
   public int Longitude { get; set; }
   public int Latitude { get; set; }
}

public class Car : ILocatable
{
   public int Longitude { get; set; }
   public int Latitude { get; set; }
}

We can use the mixin like this:

Ship ship = new Ship(); ship.MoveNorth(10); Car car = new Car(); car.MoveWest(23);

It's a very nice pattern for adding capabilities to entities.

Thursday, May 29, 2008

Upgrading Suteki Shop to MVC Framework CTP 3

I've just spent this morning upgrading Suteki Shop to the latest version of the MVC Framework; CTP 3. There are some nice touches in it, but not an awful lot has changed, especially if, like me, you've been using the Code Plex source that was recently made available. Scott Guthrie details most of the major changes with this release on his blog.

Action Result

Still, it wasn't all plain sailing, there have been a lot of API changes since the last code drop. The names of the new controller action return values have changed as have the properties on the Controller base class which return them. I had to do a global find and replace for those. I'd written some nice test helper extension methods around ActionResult which had to be upgraded.

View data changes

The changes to view data caused a lot of churn as well. Now, instead of two separate kinds of view data; dictionary and strongly typed, there's a single class ViewDataDictionary<T> that supports both, with a new Model property having the type of the type parameter. This means that every invocation of ViewData in your View has to change to ViewData.Model. Currently I factor all my view data into a strongly typed hierarchy that I can pass through to various html helpers I've written, but with the new style view data there might be an opportunity for some nice refactorings.

Select rendering changes

Anther change which brings up some nice possibilities is the separation of the DropDownList html helper extension (renamed from 'Select') from the actual building of the select list itself in a SelectList class that you can pass from the controller along with all its data. Providing select lists from an IoC container might be a nice way to factor the loading of lookup data out from the controller.

One thing caught me out though. They've changed the order of the 'value' and 'text' parameters from the old Select helper method. I got some nasty runtime errors because of this.

'Hidden' extension method changes

Another nasty was the Hidden html helper extension method that renders an '<input type="hidden">' tag. It now takes a string as its value parameter instead of an object. For some reason the aspnet compiler doesn't seem to catch this change and so my forms were failing at runtime with the value parameter as an empty string.

MVCContrib

Probably the biggest investment of time this morning was changing the MVCContrib code to work with CTP 3. No, I didn't upgrade the entire thing; I just ripped out anything I wasn't using and upgraded the rest. It wasn't too bad in the end which must be tribute to the quality of the code. Once Jeremy and co have got the whole thing working with CTP 3, I'll move back to the main branch.

It's really fun working with the MVC Framework. The pace of development has been swift and it's nice that the team don't have to bake in the API at this stage which has allowed them to progressively factor the framework with feedback from the community. Sure it means that you have to spend half a day changing your code to work with a new release every so often, but if that means the design can evolve rapidly, I'm all for it.

Monday, May 26, 2008

Compiling aspx templates using aspnet_compiler

The default build process for aspx files is a problem in web site development. By default they only build on request, so nasty compile time errors can lay hidden until you actually hit the problem page when looking at the site. This is a real pain; when I hit F6, I want everything to build.

I've only recently come across the command line tool aspnet_compiler. It simply pre-compiles all the aspx pages in your web application. You can run it as a post build event which means all your aspx templates get built each time:

aspnet_compiler_setup

Now when you build, you get aspx errors reported too. Clicking on the errors takes you to the correct place in the aspx file, just like with regular .cs code:

aspnet_compiler_errors

Now is this common knowledge that I've just somehow missed out on. It seems strange that I had to work out this trick by myself?

What is RSD? Making my Suteki Shop to Live Writer integration slicker.

I recently wrote about how nice it is to use the MetaWeblog API to allow tools like Live Writer update content on bespoke software like my Suteki Shop eCommerce application (which is now morphing into a generic CMS). The one thing I didn't like was the step that requires the user to enter the API their site exposes and it's endpoint:

3_SelectProvider

A little more digging around the Live Writer docs and various blogs led me to discover Really Simple Discovery (RSD). It's an XML schema for describing the APIs that your software exposes by Daniel Berlinger.

In the head of your homepage you have a link to the location of your rsd.xml file:

<link title="RSD" href="http://localhost:63638/rsd.xml" type="application/rsd+xml" rel="EditURI" />

And here's the file itself:

<?xml version="1.0" encoding="utf-8" ?>
<rsd version="1.0" xmlns="http://archipelago.phrasewise.com/rsd" >
 <service>
   <engineName>SutekiShop</engineName>
   <engineLink>http://sutekishop.co.uk/</engineLink>
   <homePageLink>http://localhost:63638/home</homePageLink>
   <apis>
     <api name="MetaWeblog" preferred="true" apiLink="http://localhost:63638/metablogapi.aspx" blogID="1" />
   </apis>
 </service>
</rsd>

As you can see it simply contains the web site engine name (suteki shop), the URL of the engine's homepage and the URL of this instance's homepage. Then follows a list of APIs that the engine supports. In my case it's a single entry for MetaWeblog that shows the XML RPC endpoint.

In the case of Suteki Shop, since it's an MVC Framework based application the rsd.xml file itself is generated from a view that simply inserts the current application URL where, in this case, "http://localhost:63638" appears.

Wednesday, May 21, 2008

Using Live Writer to Post Content to a Web Site

I've just added the MetaWeblog API to the Content Management System (CMS) of Suteki Shop. It's most excellent. It means that an administrator can manage the content of the web site using a slick WSYWIG tool like Windows Live Writer. I simply ripped the code for the API from Subtext, a really nice open source blogging application. It took me most of today to get it working, but it'll be a major win for my users who I can't expect to write HTML straight into a web based admin interface.

It's really easy to set up. Simply open up Live Writer and add a new account saying you want to use 'Another weblog service':

1_chooseBlogType

Then enter the URL of the application, here I'm just giving it the URL of the Visual Studio Development Web Server. You also need to enter an admin username (email) and password.

2_HomepageAndLogin

The last step is to tell Live Writer that Suteki Shop uses the Metaweblog API and point it to the XmlRpc endpoint that hosts the API.

3_SelectProvider

After that it's all set up and you can write a post, including images in live writer:

LiveWriterEdit

When you click the 'Publish' button the content magically appears with a new menu item on the Suteki Shop site:

InSutekiShop

You can find my subtext's implementation of the MetaWeblog API the usual place:

http://code.google.com/p/sutekishop/source/checkout

The MetaWeblog API is implemented in the files:

MetaWeblogAPI.cs

MetaWeblog.cs

Note the additional handler in the web.config file:

<add type="Suteki.Shop.XmlRpc.MetaWeblog, Suteki.Shop" path="metablogapi.aspx" verb="*" />

Sunday, May 18, 2008

Nested Master Pages with the MVC Framework

If you install the MVC Framework CTP2 you may notice that it doesn't come with a nested master page template, but don't worry it very easy to create your own from the existing Web Forms template. Here's how:

First add a standard (web forms) nested master page:

AddDialogue

Next, in the code behind file 'myNested.master.cs' inherit from System.Web.Mvc.ViewMasterPage:

CodeBehind

Then you are ready to go, just add a content placeholder:

markup

Thursday, May 15, 2008

MVC Framework Scaffolding

Scaffolding is an idea from Ruby on Rails. It's a way of providing a simple default implementation of common views of data in a web application. After coding a couple of Controllers for the simple administration of lookup data in Suteki Shop, I got really bored of the repetitive coding and decided to write a simple scaffolding controller. Using an IoC Container and the IRepository pattern makes this really easy. To create a new scaffolding controller simply write:

public class CountryController : ScaffoldController<Country>
{
}

And here's a screen-shot of the two country views, Index and Edit.

sutekishopCountries

sutekishopCountriesEdit

It's clever enough to work out which properties are foreign keys and get the correct lookup table to populate the 'Post Zone' combo box. It also uses my generic ordering implementation to allow you to reorder the items (that's the little green up and down arrows in the list of countries). Unfortunately, I haven't got around to auto generating the views yet, so you still have to write those.

All the code can be found in Suteki Shop as usual. But just to show how it's really quite simple, here's the full code of the ScaffoldController:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using Suteki.Shop.Repositories;
using Suteki.Shop.Services;
using Suteki.Shop.ViewData;
using Suteki.Shop.Validation;
using Suteki.Shop.Extensions;
using Castle.MicroKernel;
using System.Reflection;

namespace Suteki.Shop.Controllers
{
   public class ScaffoldController<T> : ControllerBase where T : class, IOrderable, new()
   {
       public IKernel Kernel { get; set; }
       public IRepository<T> Repository { get; set; }
       public IOrderableService<T> OrderableService { get; set; }

       public virtual ActionResult Index()
       {
           return RenderIndexView();
       }

       private ActionResult RenderIndexView()
       {
           var items = Repository.GetAll().InOrder();
           return RenderView("Index", Scaffold.Data<T>().With(items));
       }

       public virtual ActionResult New()
       {
           T item = new T
           {
               Position = OrderableService.NextPosition
           };
           return RenderView("Edit", BuildEditViewData().With(item));
       }

       [NonAction]
       public virtual ScaffoldViewData<T> BuildEditViewData()
       {
           ScaffoldViewData<T> viewData = Scaffold.Data<T>();
           AppendLookupLists(viewData);
           return viewData;
       }

       public virtual ActionResult Edit(int id)
       {
           T item = Repository.GetById(id);
           return RenderView("Edit", BuildEditViewData().With(item));
       }

       public virtual ActionResult Update()
       {
           int id = int.Parse(this.ReadFromRequest(typeof(T).GetPrimaryKey().Name));
           T item = null;

           if (id == 0)
           {
               item = new T();
           }
           else
           {
               item = Repository.GetById(id);
           }

           try
           {
               ValidatingBinder.UpdateFrom(item, Request.Form);
               if (id == 0)
               {
                   Repository.InsertOnSubmit(item);
               }
               Repository.SubmitChanges();

               return RenderIndexView();
           }
           catch (ValidationException validationException)
           {
               return RenderView("Edit", BuildEditViewData().With(item)
                   .WithErrorMessage(validationException.Message));
           }
       }

       public virtual ActionResult MoveUp(int id)
       {
           OrderableService.MoveItemAtPosition(id).UpOne();
           return RenderIndexView();
       }

       public virtual ActionResult MoveDown(int id)
       {
           OrderableService.MoveItemAtPosition(id).DownOne();
           return RenderIndexView();
       }

       /// <summary>
       /// Appends any lookup lists T might need for editing
       /// </summary>
       /// <param name="viewData"></param>
       [NonAction]
       public virtual void AppendLookupLists(ScaffoldViewData<T> viewData)
       {
           // find any properties that are attributed as a linq entity
           foreach (PropertyInfo property in typeof(T).GetProperties())
           {
               if (property.PropertyType.IsLinqEntity())
               {
                   AppendLookupList(viewData, property);
               }
           }
       }

       private void AppendLookupList(ScaffoldViewData<T> viewData, PropertyInfo property)
       {
           if (Kernel == null)
           {
               throw new ApplicationException("The Kernel property must be set before AppendLookupLists is called");
           }

           // get the repository for this Entity
           Type repositoryType = typeof(IRepository<>).MakeGenericType(new Type[] { property.PropertyType });

           object repository = Kernel.Resolve(repositoryType);
           if (repository == null)
           {
               throw new ApplicationException("Could not find IRepository<{0}> in kernel".With(property.PropertyType));
           }

           MethodInfo getAllMethod = repositoryType.GetMethod("GetAll");

           // get the items
           object items = getAllMethod.Invoke(repository, new object[] { });

           // add the items to the viewData
           viewData.WithLookupList(property.PropertyType, items);
       }
   }
}

Sunday, May 04, 2008

Credit Card Validation With LINQ

The credit card mod 10 validation algorithm is quite straightforward. Easy enough for me to have a go at writing with LINQ. This example is written as an extension method validator. Let me know if you can make this even more concise.

public ValidationProperty IsCreditCard()
{
   value.Label(label).IsNumeric().WithLengthRange(13.To(18));

   var numbers = value.Trim().Reverse().Select(c => int.Parse(c.ToString()));

   int oddSum = numbers.AtOddPositions().Sum();
   int doubleEvenSum = numbers.AtEvenPositions().SelectMany(i => new int[] { (i * 2) % 10, (i * 2) / 10 }).Sum();

   if ((oddSum + doubleEvenSum) % 10 != 0)
   {
       throw new ValidationException("{0} is not a valid credit card number".With(label));
   }

   return this;
}

Here are the AtOddPositions and AtEvenPositions extension methods:

public static IEnumerable<T> AtOddPositions<T>(this IEnumerable<T> list)
{
   bool odd = false; // 0th position is even
   foreach (T item in list)
   {
       odd = !odd;
       if (odd)
       {
           yield return item;
       }
   }
}

public static IEnumerable<T> AtEvenPositions<T>(this IEnumerable<T> list)
{
   bool even = true; // 0th position is even
   foreach (T item in list)
   {
       even = !even;
       if (even)
       {
           yield return item;
       }
   }
}

Am I going extension method crazy??

Collecting Validation Exceptions

I've been putting some thought into validation in the context of an MVC Framework application. Recently I wrote about using Extension Method Validators as a neat way of expressing validation rules in your domain entities. These validators throw a ValidationException when they trigger.

sutekishopValidationErrors

Now in a user interface we would really like to see all the validation failures for a form. So we need to collect the exceptions as they occur and then continue processing. Here's a neat little Validator class that's simply a collection of Action delegates that executes them when its Validate method is called:

public class Validator : List<Action>
{
  public void Validate()
  {
      StringBuilder message = new StringBuilder();

      foreach (Action validation in this)
      {
          try
          {
              validation();
          }
          catch (ValidationException validationException)
          {
              message.AppendFormat("{0}<br />", validationException.Message);
          }
      }

      if (message.Length > 0)
      {
          throw new ValidationException(message.ToString());
      }
  }
}

You can use it like this:

string property = "";

Validator validator = new Validator
{
  () => property.Label("Property 1").IsRequired(),
  () => property.Label("Property 2").IsRequired(),
  () => property.Label("Property 3").IsRequired()
};

validator.Validate();

Since the 'property' variable is an empty string, each call to IsRequired() will throw a ValidationException. The message of each of these exceptions gets captured and then a single ValidationException will be called with the concatenated messages.

Thursday, May 01, 2008

CSS Based Foldout Menus

sutekishopMenu

I've just added these foldout menus to Suteki Shop. These guys at seoconsultants show you how to do it here:

http://www.seoconsultants.com/css/menus/tutorial/

It's really cool because the HTML is clear nested UL/LI elements; very SEO friendly and it's all done with CSS, not a line of javascript needed. Nice!

MVC Framework - default.aspx routes no longer work with the latest CodePlex build

Here's a minor bug that really foxed me until I managed to google the answer. With the latest MVC Framework code from CodePlex the routing has changed. It's now much more flexible, but one side effect is that it only kicks in if the requested resource can't be found on disk. So if you have a placeholder (blank) Default.aspx page in the root of your project as in previous versions of the MVC Framework, it actually gets rendered. Unfortunately you need that Default.aspx page for cassini (the Visual Studio development web server) to work. The work-around is to put a Response.Redirect in the code-behind of the Default.aspx placeholder:

    public partial class _Default : Page
   {
       public void Page_Load(object sender, System.EventArgs e)
       {
           Response.Redirect("~/Home");
       }
   }

The details about this can be found at Phil Haack's blog:

http://haacked.com/archive/2008/04/10/upcoming-changes-in-routing.aspx