About your host

Photo of your host - Charles Vallance Charles Vallance is a web developer with a slight case of OCD when it comes to nice clean standards compliant html and code.

Badges

twitter / cvallance
My articles have been featured in The Morning Brew - Daily .NET News and Views

Tag Cloud

more tags...

I came across some interesting behaviour with the ASP.NET MVC 2 Beta yesterday. When I was binding to a DateTime value it would work fine with a POST request but when I switched that to a GET request, everything bombed. Weird.

After a bit more investigation I found it had to do with my culture... well, rather it had to do with the  GET request binding my date (formatted as 'dd/mm/yyyy') using the backwards American date format of 'mm/dd/yyyy' (flame on).

It was late at night at the time, so I quickly whipped up a very basic ASP.NET MVC application to reproduce the interesting behaviour I was experiencing and fired it off to Phil Haack - which in hindsight wasn't probably the best route to take, should've followed some sort of procedure and posted it to codeplex project's issue tracker instead of bothering Phil.

Anyway... after a good nights sleep, a good day at work under my belt and no reply from Phil (obviously a busy man and bigger things to worry about), I decided to have a gander at the issue myself.

After a short time of debuging into the ASP.NET MVC 2 Beta source code, I managed to find the culprit. Surprisingly the issue resided in the QueryStringValueProvider class - I know, who would've guessed?!

What I found is the following in all its undisguised glory:

// QueryString should use the invariant culture since it's part of the URL, and the URL should be
// interpreted in a uniform fashion regardless of the origin of a particular request.
protected override CultureInfo GetCulture(ControllerContext controllerContext) {
    return CultureInfo.InvariantCulture;
}

My first thought was 'hang on, so this weird behaviour is actually intentional? WTF were they thinking?!?'. Then, naturally, images of a CultureAwareQueryStringValueProvider swirlled around my head - easy 'fix' I thought. But as my testosterone levels started to drop back to normal, I realised that someone smarter than me has obviously made a very conscience decision to make it culture unaware...

So why is it culture unaware?

Well, if you think about it, a URL is a pointer to a specific resource. So if the url is culture aware, that same URL could point to different resources.

E.g. say there was a URL like:

'http://www.temp.com/events?from=01/01/2010&to=01/03/2010&page=3'

If it was culture aware, my culture would expect it to show the 3rd page of events between 1st Jan 2010 & 1st March 2010 BUT someone else's inferior culture (flame stoke) might expect it to show the 3rd page of events between 1st Jan 2010 & 3rd Jan 2010.

Making sense now? Good.

So what are my options to fix this? Well, as I see it, there are 3 obvious solutions.

Option 1: Use a POST request instead of a GET request. This really isn't an option, in my situation I really need it to be a GET request.

Option 2: Create a custom binding to make the querystring culture aware. This really isn't an option because as I outlined above, URLs shouldn't be culture aware.

Option 3: Change the date format in the querystring. I.e. use the 'yyyy/mm/dd' format so that all cultures will understand and interpret the URL the same1. This is really the only option for me and I'm relatively happy with it... from a technical perspective it's super but from a usability perspective (e.g. users having to enter dates in that format), it may not be the best.

Any other ideas dear reader?

EDIT: Got a reply from Phil and he basically backed up my understanding of the situation and gave his take on some solutions similar to those outlined above. Cheers Phil! :-)

1If someone tells me that some culture recognises the date format 'yyyy/dd/mm' I'll probably cry. Seriously.

With the new release of ASP.NET MVC 2 CTP 1 there are a lot of cool new features that are driven by html attributes on your models. Read both The Gu’s and Phil Haack’s blog posts about the release to get the full low down.

I’m guessing that (like myself) a lot of people out there are using ORM’s to generate their models. Yeah? Thought so. Anyway, looking at that previous sentence, the key word is *generate*. You see, if you put property attributes in these generated files, they’re just going to be wiped each time you make a db schema change – far from ideal! ;-)

So where do you put these attributes?

Nope, you can’t put them in a partial class because C# and VB.NET don’t allow you to add attributes to properties defined in another partial class.

Enter the Buddy Class!

The Gu touched on buddy classes in his aforelinked1 post, but didn’t go into them in any detail – so I’m going to try and fill that void with this post.

Say we have a 'Customer' table (similar to The Gu's example) in our database and our orm generates a class like so:

public partial class Customer
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public int Age { get; set; }
    public string Email { get; set; }
    public string Country { get; set; }
}

Before we go any further (if you haven’t already) you should add a reference to System.ComponentModel.DataAnnotations.dll to your project – you’re gonna need it.

Next we create a partial class for the generated class and also create it’s buddy class.

The best way to explain this is with code, so here you go:

[MetadataType(typeof(Customer_Metadata))]
public partial class Customer
{
    //Custom model stuff here
}

public class Customer_Metadata
{
    [DisplayName("First name")]
    [Required(ErrorMessage = "First name is required")]
    public string FirstName { get; set; }

    [DisplayName("Last name")]
    [Required(ErrorMessage = "Last name is required")]
    public string LastName { get; set; }

    [Range(1, 120, ErrorMessage = "Invalid Age")]
    public int Age { get; set; }

    [Required(ErrorMessage = "Email is required")]
    [RegularExpression(@"^([a-zA-Z0-9_\-\.]+)@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.)|(([a-zA-Z0-9\-]+\.)+))([a-zA-Z]{2,4}|[0-9]{1,3})(\]?)$")]
    public string Email { get; set; }

    [Required(ErrorMessage = "Country is required")]
    [UIHint("CountryDropDown")]
    public string Country { get; set; }
}

Savvy? Good, because that's really all there is to it! ;-)

1aforelinked - new word? © Charles Vallance 2009

EDIT: Making the buddy class a private inner class

As outlined below by Tyrone and Steve, it's probably best practice to have the buddy class as a private inner class.

E.g.

[MetadataType(typeof(Customer.Metadata))]
public partial class Customer
{
    //Custom model stuff here

    private sealed class Metadata
    {
        [DisplayName("First name")]
        [Required(ErrorMessage = "First name is required")]
        public string FirstName { get; set; }

        [DisplayName("Last name")]
        [Required(ErrorMessage = "Last name is required")]
        public string LastName { get; set; }

        [Range(1, 120, ErrorMessage = "Invalid Age")]
        public int Age { get; set; }

        [Required(ErrorMessage = "Email is required")]
        [RegularExpression(@"^([a-zA-Z0-9_\-\.]+)@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.)|(([a-zA-Z0-9\-]+\.)+))([a-zA-Z]{2,4}|[0-9]{1,3})(\]?)$")]
        public string Email { get; set; }

        [Required(ErrorMessage = "Country is required")]
        [UIHint("CountryDropDown")]
        public string Country { get; set; }
    }
}

I just finished writing an epic (in length) answer on StackOverflow and thought that it is nearly blog worthy… so in keeping with my mediocre posts, I’m going to blog it! ;-)

The question was this:

My ultimate goal is to have a menu that adds a class to the list item that associates with the current page I am on.

So I have it set up such that each controller will be associated with an item in my menu. I need to add a class to that list item (changing the color, background, whatever).

Is there a simple way to do this? Pass a value to the View, then what?

It seems like a good idea to pass a value to the view, but if your menu items are  coupled to your controllers and actions then it would make sense to simply use the values you can find in the ViewContext.RouteData.Values collection.

So using the following simple HtmlHelper extension as a base we can cater for any number of scenarios.

public static string OnClass(this HtmlHelper html, bool isOn)
{
    if (isOn)
        return " class=\"on\"";

    return string.Empty;
}

Just test the current action:

public static string OnClass(this HtmlHelper html, string action)
{
    string currentAction = html.ViewContext.RouteData.Values["action"].ToString();

    return html.OnClass(currentAction.ToLower() == action.ToLower());
}

Test for any number of actions:

public static string OnClass(this HtmlHelper html, string[] actions)
{
    string currentAction = html.ViewContext.RouteData.Values["action"].ToString();

    foreach (string action in actions)
    {
        if (currentAction.ToLower() == action.ToLower())
            return html.OnClass(true);
    }

    return string.Empty;
}

Test for action and controller:

public static string OnClass(this HtmlHelper html, string action, string controller)
{
    string currentController = html.ViewContext.RouteData.Values["controller"].ToString();

    if (currentController.ToLower() == controller.ToLower())
        return html.OnClass(action);

    return string.Empty;
}

Test for any number of actions on a controller:

public static string OnClass(this HtmlHelper html, string[] actions, string controller)
{
    string currentController = html.ViewContext.RouteData.Values["controller"].ToString();

    if (currentController.ToLower() == controller.ToLower())
        return html.OnClass(actions);

    return string.Empty;
}

Etc etc.

Then you simply call any of them from your view like so:

<ul id="left-menu">
    <!-- simple boolean -->
    <li <%= Html.OnClass(something == somethingElse) %>>Blah</li>
    <!-- action -->
    <li <%= Html.OnClass("Index") %>>Blah>/li>
    <!-- any number of actions -->
    <li <%= Html.OnClass(new string[] { "Index", "Details", "View" }) %>>Blah</li>
    <!-- action and controller -->
    <li <%= Html.OnClass("Index", "Home") %>>Blah</li>
    <!-- any number of actions on a controller -->
    <li <%= Html.OnClass(new string[] { "Index", "Details", "View" }, "Home") %>>Blah</li>
</ul>

So yeah, that was the approach I used in my last ASP.NET MVC application. No doubt next time I’ll do something completely different… maybe create my own versions of HtmlHelper.ActionLink that create the <li> & <a> & set the on class... mmm yeah, I like that idea.

I currently have a situation where I have two separate actions that rendered the same view and then on this view it has two individual forms which post to the two aforementioned controller actions.

Each of the forms have their own separate ValidationSummary() calls to display their validation errors. But when I posted to either one of these actions and the validation failed, I quickly realised a short coming of the built in ValidationSummary() helper method… you see, it doesn’t have a clue about which form has the errors!

Confused yet? Oddly enough, so am I.

A screen shot should clear things up:

register-login

And the fail:

register-login-fail

Making sense now? Good...

Anyway, I came up with an easy enough work around so I thought I would share it.

namespace System.Web.Mvc
{
    public static class HtmlExtensions
    {
        public static string ActionValidationSummary(this HtmlHelper html, string action)
        {
            string currentAction = html.ViewContext.RouteData.Values["action"].ToString();
            
            if (currentAction.ToLower() == action.ToLower())
                return html.ValidationSummary();
            
            return string.Empty;
        }
    }
}

I'm (basically) calling it like so:

<h2>Register</h2>

<%= Html.ActionValidationSummary("Register") %>

<form method="post" id="register-form" action="<%= Html.AttributeEncode(Url.Action("Register")) %>">

... blah ...

</form>


<h2>User Login</h2>

<%= Html.ActionValidationSummary("LogIn") %>

<form method="post" id="login-form" action="<%= Html.AttributeEncode(Url.Action("LogIn")) %>">

... blah ...

</form>

And that's it. Enjoy.

Technorati Tags:

I’ve got myself a brand spanking new VPS. w00t!

So with some help of the ‘oh so clever’ infamous Claw @ Signify I’ve got my DNS server up & running and everything seems to be going just super*.

Along with the new VPS I’ve decided to upgrade my blog from Wordpress to Subtext. Which, surprisingly, wasn’t that easy consider Wordpress doesn’t natively export to BlogML (there really should be standard between all blog engines).

Anyway, I got an export working using the BlogML project on CodePlex along with some additional hacking of the resulting XML file. So all my previous, extremely out of date, posts are back if anyone is still using ASP.NET MVC CTP’s. Ha.

As for the new blog, after some rather exhausting searching for a decent skin for my new Subtext install, I couldn’t find anything that I really liked so I’ve decided to port the Lazy Days skin by Fullahead with inspiration from Steven Harman and his Subtext port. So far so good, but as you can tell I’ve got alot more to go!

More updates to come - to the skin and to my blog that is. I, hesitantly, promise to update more often than I have been (last update January 2008!). Focus will be on the best thing to come out of Microsoft in years – ASP.NET MVC.

*Yet to fully setup hMailServer – how hard can it be? ;-)

Technorati Tags:

Ok, after a slight push from Travis, I decided to put aside some time to live up to my promise and post an entry on how to enable extensionless requests on the new ASP.NET MVC framework using IIS 5.1 & 6.

I'm starting where my last post, ASP.NET MVC on IIS 5.1 (XP Pro), left off. So if you haven't got the MVC framework working on IIS 5.1 or 6 with extensions, have a gander at my last post and make sure its all working.

What we currently have (following after my last post) is links like /Home.mvc/About which are working because we've mapped the .mvc extension to the ASP.NET application, so IIS knows it's for ASP.NET and can fire it off in that direction, but what we'd ideally want is just having the link look like /Home/About (without any extension). Currently if you try an extensionless request like that, IIS just doesn't know what to do with the request or where to send it, so it returns a 404.

Enter URL rewriting!

I'm going to use Helicon Tech's ISAPI_Rewrite 3. The lite version, which I'm going to use, is free but has limitations (which you can read about here) but should be fine for what we want to do. The full version is only $99 US per server... cheap as chips if you ask me!

ISAPI_Rewrite offers the same syntax and behavior as mod_rewrite on Apache (which is a good thing :-). If you don't know anything about mod_rewrite or are completely new to the whole URL Rewriting idea, I would recommend you read up on some material - here would be a good place to start, or good old wikipedia.

Anyway, lets start.

First off you'll want to download ISAPI_Rewrite 3 from here (just grab the lite version) and install it. You shouldn't run into any problems... touch wood. If you do, go back to their documentation for troubleshooting advice.

Once it's installed, open up the httpd.conf file in your favourite text editor (Notepad++ is my fav :-)) and add the following lines. Note that if you used the .msi installer, the httpd.conf file should be in the Program Files\Helicon\ISAPI_Rewrite3\ directory.

RewriteEngine on 
RewriteBase 
RewriteRule ^Home/(.*?)$ Home.mvc/$1

What this RewriteRule is doing is intercepting any request that starts with "Home/" and replaces the "Home/" part with "Home.mvc/". Once it's done this, IIS handles the request and sees the .mvc extension, so it fires that request off to ASP.NET.

Brilliant.

So go back to your browser and try and visit the http://localhost/Home/About address on your local machine. Hopefully you should be met with the same page as if you would have written http://localhost/Home.mvc/About. Yay! Now do that for all your different controllers... Just make sure none of your controllers are called Styles, CSS, Scripts etc etc ;-)

But there is still a problem... anyone picked it yet?

You see, our incoming request, "/Home/About", is being rewritten, by ISAPI_Rewrite, to "/Home.mvc/About" and is then being passed to ASP.NET and it's then being handled by the ASP.NET MVC routing engine. It should then match the following routing rule:

RouteTable.Routes.Add(new Route
{
    Url = "[controller].mvc/[action]/[id]",
    Defaults = new { action = "Index", id = (string)null },
    RouteHandler = typeof(MvcRouteHandler)
});

So this will go off to the "About" action on the "Home" controller. Fantastic... but what happens when you go the other way? What happens when you get the ASP.NET MVC routing engine to write the links for you? That's right, it'll have the .mvc extension thrown onto the end of any requests that match the rule.

Hmmm, not cool because it basically renders all of what we've done with the URL rewriting useless!

Not all is lost though. You see, the ASP.NET MVC routing engine tests each incoming and outgoing URL against the RouteTable from top to bottom (which is why you need to write them from most specific to least specific)... So if we throw in a different rule, say the exact same rule without the .mvc part, that should be evaluated first and find it as a match and then the outbound links shouldn't have .mvc in them.

Something like this:

protected void Application_Start(object sender, EventArgs e)
{
    RouteTable.Routes.Add(new Route
    {
        Url = "[controller]/[action]/[id]",
        Defaults = new { action = "Index", id = (string)null },
        RouteHandler = typeof(MvcRouteHandler)
    });

    RouteTable.Routes.Add(new Route
    {
        Url = "[controller].mvc/[action]/[id]",
        Defaults = new { action = "Index", id = (string)null },
        RouteHandler = typeof(MvcRouteHandler)
    });

    RouteTable.Routes.Add(new Route
    {
        Url = "Default.aspx",
        Defaults = new { controller = "Home", action = "Index", id = (string)null },
        RouteHandler = typeof(MvcRouteHandler)
    });
}

Glorious.

So, that's a start... it's nice and simple and hopefully a good place to get you going. I'm open to suggestion as to different ways of going about it... I'm no mod_rewrite or regex guru by a long shot!

Technorati Tags: ,

Ok, so while Scott Hanselman's MVC How-To Screencast is downloading, I thought I'd write a quick blog about getting ASP.NET MVC working on XP Pro and IIS 5.1. Although I haven't tested it, this fix should also work on IIS 6 boxes as well. When you create a default ASP.NET MVC project and use IIS 5.1 on XP Pro to serve up the pages, the default page (http://localhost/) loads up just fine but when you try and navigate to the 'About Us' page you'll get a 'The page cannot be found' 404 error. To remedy this (without going down the ISAPI filter road to get extensionless requests cranking) and use the .mvc extension as suggested, read on. Firstly you must edit the Global.asax.cs file and change the default routing rule to the following (you're just adding .mvc after [controller]).

RouteTable.Routes.Add(new Route
{
    Url = "[controller].mvc/[action]/[id]",
    Defaults = new { action = "Index", id = (string)null },
    RouteHandler = typeof(MvcRouteHandler)
});

Now after you've done this you'll have to associate the .mvc extension to ASP.NET. Firstly you must get to the properties of your site by right clicking on your 'Default Web Site' and selecting 'Properties' IIS51-site-properties

Then on the Home Directory tab, hit the Configuration... button

IIS51-site-configuration

To map the .MVC extension to the asp.net engine, we must find the location of the asp.net executable. The easiest way of doing this is to find where the .aspx extension is mapping to.

So to do this, on the 'Mappings' tab, on the 'Application Configuration' popup, find the '.aspx' extension and hit 'Edit'. From this box, copy the full executable location like so.

IIS51-excutable-copy

After you've done that, hit 'Cancel' and you should be back at the 'Application Configuration' popup. From this screen hit the 'Add' button and paste in the previous Executable location, then fill out the rest of the details like the screen shot below and make sure that the 'Check that file exists' check box is NOT ticked.

IIS51-extension-association

After that you should be ready to rock.

The great thing about the ASP.NET MVC routing engine is that you won't need to change any of the links, they will now be updated so that they include the .mvc extension, e.g. http://localhost/Home.mvc/About

Not pretty or ideal but it's quick and it works! I'll try and get a extensionless fix up soon.

Technorati Tags: ,,

I received an early birthday present from the Microsoft crew today... they've released the ASP.NET 3.5 Extensions CTP! Yeow! You can get it here.

Scott Guthrie has just blogged about the release and also has quite a few good links. Go here to get the low down from him.

It was also good to see that Scott Hanselman had left a comment to say he was in the process of recording a 40 minute video show how to make a sample site using ASP.NET MVC. He also said that we shall be flooded with samples soon. Bring on the downpour!

As I write this I'm in the process of installing everything thought I would share a couple of tid bits and tips.

Firstly, it was funny to see that after the install had been going for a while, down the bottom of the screen the message read - "The installation is taking longer than expected." Good to see someone having a laugh. :-)

Because I wanted to play around with ADO.NET Entities (& LINQ to Entities) as well, I needed to install the ADO.NET Entity Framework tools that are compatible with this latest ASP.NET 3.5 Extensions CTP release. You can get that from here. But for most of you, before you install that you'll have to patch your VS 2008 install... You can get that patch here.

You'll also want to grab the MVC Toolkit aswell, it provides a whole lotta extra helpers and from first glance, also contains a sample project for a blog application. You can get the MVC Toolkit here.

That's all from me right now... I'm off to play with my new toy!

Technorati Tags:

Just updated Wordpress and the final step in the upgrade process is posting a new post about upgrading it. Hmmm... maybe another time, there are better things afoot!

MS MVC Code

Scott Hanselman is teasing me. He's posted the source code, DevConnections and PNPSummit MVC Demos Source Code, from the MVC demos that he and others have performed. It obviously doesn't work or compile *sigh*. I'm quite curious how the validation is going to work... so far I haven't seen any examples of it.

He says there will be a CTP very soon... still can't wait! :-)

LOLCode on the DLR

Scott has also recently blogged about some smart cookie who implemented LOLCode on the DLR. Totally pointless but highly amusing. View the blog here - The Weekly Source Code 11 - LOLCode DLR Edition.

Which brings me on to the subject of LOLCats.

I feel the my place of work has gone slightly LOLCats mad recently... and rightly so I guess, they are rather funny. This is one of the current favourites - teenage mutent nenja kitteh.

Speaking of funny stuff on the web... everyone probably knows this already, but you really can't go past XKCD for a good old chuckle. These two, Exploits of a Mom and A-Minus-Minus, would have to be amongst my favourites.

In other news...

My parents Manchester Terrier has given birth to 3 healthy little bitches. They are looking for good homes, so gimme a bell if you're interested.

I got to see them in the weekend but they were only 2 days old and still had their eyes closed and will do for another 6 days or so apparently. Sure, they were cute... but at that age and stage they aren't really up to much. I can't wait to see them again in a couple of weeks time... they'll be little pockets of fun!

Technorati Tags: ,

Ahhh... now I remember, I've got a blog! I knew I would find it hard to post to this thing, so hard to find the time! Maybe now the ski season is over (bring on next year!) I can find a bit more time.

Castle

As I have posted before, I have been experimenting with the castle stack for a little while now and I really enjoy it. Although, at the same time I just feel something is a miss...

Maybe it was my decision to go with brail and the debugging nightmare that came with it. In hindsight, I probably should have gone with the AspView engine - It just seems to make more sense to me.

Maybe it's the fact that documentation is hard to find. Sure, it's out there... but it seems to be scattered all over the place (at least that's been my experience). You can't blame them thou, it is an open source framework and I know I'd be a darn shot happier coding than I would be writing documentation!

I must say there is a great mailing list thou... questions tend to get answered there fairly quickly.

ASP.NET MVC Framework

I'm extremely excited about the ASP.NET MVC Framework that Microsoft is developing, I'm hoping it'll bring the best of the Castle and ASP.NET worlds together.

Scott Guthrie announced it the ASP.NET MVC Framework here (read up on it if you haven't already). At one stage of the post he says

I'll be doing some in-depth tutorial posts about the new ASP.NET MVC framework in a few weeks once the bits are available for download (in the meantime the best way to learn more is to watch the video of my Alt.net presentation).

The part I wanted focus on was - 'in a few weeks'. In my book, 'a few weeks' means 3 weeks give or take 1 week (2 max!) and it's been 24 days since Scott posted it... so hopefully, just hopefully, I'll have something to play with in a day or two. It'll also give me something to blog about! ;-)

In the mean time, I've watched both ScottGu's and ScottHan's videos from the ALT.NET conference. You can find them here on ScottHan's blog. I actually had problems watching them so I downloaded them instead. You can get them here (ScottHaOnDLRandMVCatALTNET.wmv - 130mb) and here (ScottGuOnMVCatALTNET.wmv - 440mb).

Thats about all from me tonight. A few exciting things may be happening soon... but I can't tell you about that just yet. Watch this space! ;-)

Technorati Tags: ,,