November 2008 Entries

Post-Redirect-Get Pattern in MVC

I found a good write-up of the PRG pattern in MVC by Matt Hawley this week, and have decided to use it in an MVC project I'm working on.  I have made a few changes to Matt's code however. 

1 - Use ModelBinders and ModelState to Simplify Code

Firstly, as the new version of MVC (the beta release) supports Model Binders, I updated the example to use these instead.  Now we can just save the ModelState into TempData in one go, instead of saving the error messages and the users input, so the Submit action looks something like:

public ActionResult Submit()
{
    //OMITTED: Do work here...
    if (!ModelState.IsValid)
    {
        //Save the current ModelState to TempData:
        TempData["ModelState"] = ModelState;
    }
}

In the Create action we just need to pull these values out of TempData and add to the ModelState.  MVC will then enter the users input back into the textboxes for you.  The Create action looks like:

public ActionResult Create()
{
    //If we have previous model state in TempData, add it to our
    //current ModelState property.
    var previousModelState = TempData["ModelState"] as ModelStateDictionary;
    if (previousModelState != null)
    {
        foreach (KeyValuePair<string, ModelState> kvp in previousModelState)
            if (!ModelState.ContainsKey(kvp.Key))
                ModelState.Add(kvp.Key, kvp.Value);
    }

    return View();
}

(You will want to wrap this boiler plate code up in a helper class or something though)

2. Fix Scenario where user input will be lost

Secondly, I did find a small issue with the code as posted, when the user does the following:

GET the page with a form
POST the form with invalid input
REDIRECT back to the page (with the users input intact)
REFRESH the page - the users input is now lost!

I'm not sure this is a particularly common scenario, but losing the users input is never a good way to instil trust in your application!  The reason the data is lost in this case is because we stored the users input in TempData which only exists for this request and the next one.  I thought about putting the value into Session instead, but then you'd have to come up with a strategy for removing the items at the right time (you wouldn't want the form to remember it's values from the last time it was used for instance).  In the end I decided that just putting the values back into TempData would be the best solution.  This requires the following line to be added to the Create action:

public ActionResult Create() 
{ 
    //If we have previous model state in TempData, add it to our 
    //current ModelState property. 
    var previousModelState = TempData["ModelState"] as ModelStateDictionary; 
    if (previousModelState != null) 
    { 
        foreach (KeyValuePair<string, ModelState> kvp in previousModelState) 
            if (!ModelState.ContainsKey(kvp.Key)) 
                ModelState.Add(kvp.Key, kvp.Value); 
        TempData["ModelState"] = ModelState;  //Add back into TempData
    } 

    return View(); 
}

If the user now refreshes the page after a validation failure, they will no longer lose their input.  If they go on to fix the validation errors and submit the form, the saved TempData value will be automatically cleared by the MVC framework.

DDD7 - My Thoughts

Yesterday, I attended my first DeveloperDay at the Microsoft Campus in Reading.  Below are the sessions that I attended and my thoughts on them:

TDD and Hard-To-Test Code - Ian Cooper

The first session of the day was about testing code which is hard to test.  The speaker, Ian Cooper, clearly had a lot of experience and knowledge about the subject.  Unfortunately however, I felt that there wasn't really enough code.  Most of the presentation was about the sort of code that is hard-to-test, I was hoping for more help with how to test that code.  Having said that, I still found the talk informative and interesting.  The concept of 'Seams' was something I hadn't come across by that name before (see here for a good example), although it is basically the Open/Closed Principle.

ASP.NET MVC - Show me the code - Steve Sanderson

Having followed MVC from the first preview, I was very interested in this presentation.  The format was very much about code not PowerPoint which I personally liked.  I thought Steve was extremely knowledgeable about MVC, and he really showed how easy MVC can be to create a fully functional website.  Despite not really learning much from the presentation (I'd used most of the code he used already myself), I really enjoyed the fast pace of it.  I think anyone in the audience that hadn't already used MVC will have downloaded it by now!  He sold it very well.  Definitely a speaker to look out for the in future.

Steve also has an MVC book coming out soon - definitely one for the wish list!

ASP.NET 4.0 - TOP SECRET - Phil Winstanley and Dave Sussman

This was my most eagerly anticipated session.  However, it ended up being a bit of a disappointment.  The two speakers were unable to talk much about ASP.NET v4 as Microsoft didn't announce them at the PDC.  Although they did show some nice features of VS 2010 (adornments look very nice!), it seemed as if the presentation was put together in a rush.  I didn't feel that there was very much 'meat' in the talk, and it was only the ability of the presenters to make the audience laugh that kept it going.

Oslo, Microsoft's vision for the future of Modelling - Robert Hogg

An interesting session about Oslo, something I didn't know too much about.  The subject was clearly too big to talk about in just one hour, but Rob did cover quite a few things.  Oslo's main aim is to increase pretty much everything by TEN TIMES!  Productivity, performance, everything!  Bold aims.  It'll be interesting to see how much of it they can achieve.  The idea of improving the communication between BA's, Architects and Dev's could make a huge difference to the future of the industry if they can pull it off.  Oh, and the modelling tool Quadrant looks like it could become a showpiece WPF application.  Very shiny!

The bleeding edge of web - Helen Emerson

This was a good presentation to finish the day on, as my brain was starting to hurt!  Didn't contain anything earth-shattering, but was an excellent introduction to the new features we can expect to have in the future versions of browsers.  Just a shame that it will probably take years for all browsers to implement them (I'm looking at you IE!).  Overall, a fun talk which Helen improved by getting some good audience participation.

Overall Thoughts

I enjoyed my first DeveloperDay overall.  I think in the future I will try to attend the sessions that I'm unfamiliar with, as I believe I will get more out of it that way - they are overviews rather than training sessions after all, so if you already have an overview of a technology pick a topic you don't know instead.

Enjoyable day though, and all of the speakers did a brilliant job.

AutoTabExtender AJAX Control

I have just uploaded my AutoTabExtender control to my Projects page.  Please feel free to take a look at the code and download it if it's useful for you.

I decided to create this control when I had a broken hand.  While entering my memorable to login to my online bank account, I realised that having to press 'tab' to move from day to month to year was really slowing me down with only one typing hand!!  This control can extend a textbox so that focus automatically moves on when the length of the text entered equals the MaxLength property.  The usage of the extender is as follows:

<asp:TextBox runat="server" ID="txtPart1"MaxLength="2" Columns="2"></asp:TextBox>
<asp:TextBox runat="server" ID="txtPart2"MaxLength="2" Columns="2"></asp:TextBox>
<asp:TextBox runat="server" ID="txtPart3"MaxLength="2" Columns="2"></asp:TextBox>

<spl:AutoTabExtender runat="server" ID="ate1" TargetControlID="txtPart1" NextControlID="txtPart2"></spl:AutoTabExtender>

<spl:AutoTabExtender runat="server" ID="ate2" TargetControlID="txtPart2" NextControlID="txtPart3"></spl:AutoTabExtender>

 

So, you need an extender for each textbox that should auto-tab.  You then need to specify the NextControlID to indicate to which control the focus should be moved.  In the example above I have used a form for entering a sortcode, so we need an extender for the first and second textboxes.  The extender will also allow 'natural' deleting between textboxes.  See the live demo for this here.

The extender should work in IE, FF and Chrome.

Using Castle's Dynamic Proxy Part 2 - Using Mixins

As promised, this is my follow up post to this post.  This time I will show how to use the DynamicProxy library with mixins.  Using mixin's you can add functionality to an object at runtime.  In this example, I will continue the lazy load theme from the previous post.

Imagine we have a contacts application, which contains a Person object.  This all works splendidly until one day we are asked to create a sales application.  This application must use our existing repository of contacts, but cannot use the same database.  In this new application we derive a new class from Person, with a few new attributes defining how the customer is to be treated:

public class Customer : Person
{
    public bool ApplyDiscount { get; set; }
    public bool AllowFreeDelivery { get; set; }
}

Now, to load a Customer, we must first load the main person data from the contacts database, and then load the extended data from the new sales database.  This means that each time a Customer object is loaded, there will be two database calls (no, we can't use linked servers to join the databases!).  Most of the time, this would be fine, but if you don't need the extended Customer information, then it's still a wasted database call.  (I know this sounds like a very contrived example, but I have really worked on a project where we needed to do just this).

How can we get round this?  Well, in a similar way to the previous post, we need to dynamically load the data when it's requested.  In the previous example however, when we intercept to 'get' call, we check to see if the data has already been loaded to prevent it hitting the database every time we get the property.  However, in this case the Customer object only has boolean fields - we can't check these to see if the data has already been loaded as neither true nor false imply that we haven't loaded the properties from the database.  We could change the properties to use nullable boolean's (bool?  in c#), then the lazy load method could check that one of the properties was null before loading the data.  Alternatively, we could add a boolean field called isLoaded to the class and check that instead.

The problem with both of these solution though is that we would then have data-access concerns in our entity model.  This is not good SOC!  The solution we are going to use is to add the isLoaded flag at runtime!  We will not have to make any modifications to the Customer class defined above.  To do this, we need to add the following interface and implementation:

public interface ILazyLoad
{
    bool IsLoaded { get; }
}

public class LazyLoadImpl : ILazyLoad
{
    bool _isLoaded;
    public bool IsLoaded
    {
        get { return _isLoaded; }
    }
}

Now we need our Customer object to implement this interface, so when we create a new object we instead tell Castle to create us a proxy (as per previous post), but also add the ILazyLoad interface:

//Create a proxy object instead of a standard Customer object.
Castle.DynamicProxy.ProxyGenerator a = new Castle.DynamicProxy.ProxyGenerator();
//Create an instance of our Lazy Load Implementation and pass that to Castle.
ILazyLoad mixin = new LazyLoadImpl();
ProxyGenerationOptions pgo = new ProxyGenerationOptions();
pgo.AddMixinInstance(mixin);
Customer c = a.CreateClassProxy(typeof(Customer), pgo, new CustomerInterceptor());

Now our CustomerInterceptor can intercept calls to the 'get' properties of the Customer object, then cast the Customer instance to ILazyLoad and check the value of the IsLoaded property to see if the data needs to be loaded.  If it does, then the data is retrieved and the properties are set.  The _isLoaded field is then set to 'true' using reflection.  Next time we access a property, the interceptor will know that we've already loaded the data so won't need to hit the database again.  We now have a Customer object that loads it's data only when required.  (Please refer to the previous post to see how to setup the interceptors).

 

Note:  This example uses DynamicProxy v1.  I believe version 2 of DynamicProxy works in the same way, but having not personally used it I can't guarantee this.