January 2012 Blog Posts

Data Type Validation and Model Binding in ASP.NET MVC

When validating input in a web forms application, you need to validate data types on the client and server side because you're working with text boxes and server controls. When you move to MVC, the client-side validation is still an interesting problem to solve, but the server-side validation all happens as a by-product of model binding.

The DefaultModelBinder has some special built-in provisions to handle data type parsing errors and automatically convert those into standardized model state errors.

If you're writing a custom model binder and you want to participate in this...

  • Get the value to parse from the value provider. If there is no value to parse, return null and you're done.
  • Create a ModelState object and set the Value property to the value you're about to parse.
  • Add the ModelState object to the incoming ModelBindingContext.
  • Attempt to parse the value. If it's successful, great. Return the properly parsed value and you're done.
  • If you can't parse the value...
    • Add a FormatException to the ModelState object.
    • Return null.

A simple skeleton binder that does all that looks like this:

using System;
using System.Web.Mvc;

namespace MyNamespace
{
  public class MyBinder : IModelBinder
  {
    public object BindModel(
      ControllerContext controllerContext,
      ModelBindingContext bindingContext)
    {
      var valueResult = bindingContext.ValueProvider.GetValue(bindingConext.ModelName);
      if(valueResult == null)
      {
        return null;
      }
      var modelState = new ModelState
      {
        Value = valueResult
      };
      bindingContext.ModelState.Add(bindingContext.ModelName, modelState);

      // Try to parse the value.
      object parsedValue = null;
      if(!TryParseValue(valueResult, out parsedValue)
      {
        // If you can't parse it, add a FormatException to the error list.
        modelState.Errors.Add(new FormatException());
      }

      // On success, return the parsed value; on fail, return null.
      return parsedValue;
    }
  }
}

The key part of that is the FormatException. After all is said and done, the DefaultModelBinder goes through the model state errors and finds all of the FormatExceptions that have been added. For each one it finds, it removes the FormatException and replaces it with a standardized message in the format:

The value '{0}' is not valid for {1}.

The {0} parameter is the original value result; the {1} parameter is the display name of the model/property being parsed.

The question then becomes: How do you localize/customize the data type validation message?

The DefaultModelBinder uses resources in the System.Web.Mvc assembly for its default set of error messages. There are two resource IDs to be aware of:

  • PropertyValueInvalid: The message for a value that couldn't be parsed and resulted in a FormatException. This gets a String.Format call on it where the first parameter is the attempted value and the second parameter is the name of the property. Default value: The value '{0}' is not valid for {1}.
  • PropertyValueRequired: The message for a value that wasn't available but is required, like a null sent in for an integer. No String.Format on this happens. Default value: A value is required.

If you want to use your own strings, you need to set the DefaultModelBinder.ResourceClassKey static property.

DefaultModelBinder.ResourceClassKey = "MyResources";

Once you do that, whenever one of these resources is required, the DefaultModelBinder will use HttpContext.GetGlobalResourceObject using the resource class key you provided and the ID noted earlier. Looking up the PropertyValueInvalid resource would effectively be a call to:

HttpContext.GetGlobalResourceObject("MyResources", "PropertyValueInvalid");

If you don't have the value in your resources, DefaultModelBinder will fall back and use the default version.

Unfortunately, you can't change the resource IDs, the string formatting arguments, or anything else... but at least you can change the messages.

Explore From Here - Command Line

I'm a big fan of the "command prompt here" context menu extensions for Windows Explorer. I use them all the time. Sometimes, though, I need to go the other-way-around.

That is, I'm at a command prompt and I want Windows Explorer open at the current location of my prompt.

explorer %CD%

Pretty simple, but super helpful. I had one of those "man, I'm stupid" moments when I put two-and-two together on this.

I ended up making a little batch file "explore.bat" and stuck it in my path.

@echo off
echo Opening Explorer on %CD%
explorer %CD%

So now it's just "explore" at the prompt and magic happens. (Yes, I do realize it's only five characters shorter, but I also get a nice little echo message to tell me what's going on, plus I don't have to remember it anymore.)

Note you can get some slightly different functionality if you use some of the command line switches for Windows Explorer, but for me, this works.

Choosing an Exception Type When Unit Testing Error Handling

When I'm testing exception handling code, I have tests for exceptions I know I need to handle and tests for exceptions I'm not expecting.

For example, say I have a component that calls a WCF service. If there's a communication issue, I want to mask that and return some stub/placeholder data. If there’s some other issue, I want to just let the exception bubble up and be handled by a global error handler. Something like this:

public DataObject GetData(SomeParameter p)
{
  if(p == null)
  {
    throw new ArgumentNullException("p");
  }
  DataObject data = null;
  try
  {
    data = this.SomeService.GetRealData(p);
  }
  catch(CommunicationException)
  {
    data = new StubData();
  }
  return data;
}

Not a complex scenario. I'll probably end up with a test component where I can set an exception to be thrown, or use Typemock Isolator to mock a response in the test, like:

Isolate
  .WhenCalled(() => component.SomeService.GetRealData(null))
  .WillThrow(new CommunicationException());

Then you could do your test, like:

[Test]
public void HandlesCommmunicationException()
{
  var component = CreateTheComponent();
  Isolate
    .WhenCalled(() => component.SomeService.GetRealData(null))
    .WillThrow(new CommunicationException());
  var p = new SomeParameter();
  var data = component.GetData(p);
  Assert.IsInstanceOf<StubData>(data);
}

That works well for testing the known exception type. What about the unknown type? You'll have a test like this:

[Test]
public void OtherExceptionsBubbleUp()
{
  var component = CreateTheComponent();
  Isolate
    .WhenCalled(() => component.SomeService.GetRealData(null))
    .WillThrow(new SOME_EXCEPTION_TYPE_HERE());
  var p = new SomeParameter();
  Assert.Throws<SOME_EXCEPTION_TYPE_HERE>(() =>component.GetData(p));
}

Pick an exception type that you'd never expect to get during normal execution.

Which is to say, if I wanted to see what happens when my component throws something other than a CommunicationException, I'm not going to pick something I might see for real.

I WOULD NOT pick...

  • ArgumentNullException
  • ArgumentException
  • NotSupportedException
  • InvalidOperationException

...or any other sort of "commonly used" exceptions that you might see arise from argument validation or something else.

Why not?

Let's use ArgumentNullException as an example. Say you add some more validation to the GetData method so that it inspects values in the SomeParameter p coming in. If there's a specific null value found, you throw an ArgumentNullException. You add tests for that and life is swell.

Except... you didn't remember to modify the test for your exceptions bubbling up. And, hey, look, it still passes! But it passes for the wrong reason. It's never actually getting to the service call where you think you're testing.

Instead, I WOULD pick...

  • DivideByZeroException
  • InvalidTimeZoneException

...or some other exception that you'd never expect to see in the context of what you're doing. Obviously you'll have to adjust based on what you're doing – if you're doing division in your method, you may actually get a DivideByZeroException, so you wouldn't use that.

By choosing the right exception, regardless of the refactoring of the class, your test will still pass... and it will pass for the reason you think.

Mini Cupcake Maker Cookies

Mini cupcake makerIf you happen to have one of these mini cupcake makers, you can put a dollop of cookie dough in each of the cupcake spots and 11 minutes later – cookies.

You may have to “pop” the tops on them at around 8 minutes. They usually bubble a bit.

There was some highly scientific culinary experimentation involved in arriving at that 11 minute point. Earlier and they just fall apart; later and the cookies are pretty hard.

Note we didn’t use liners, we just threw the dough right in.

Oh, and they don’t turn out pretty. But tasty!

This Isn't Scriptable

this isn't scriptable

there is no template
file new
automated
fill-in-the-blanks
cookie cutter
copy/paste
pattern
all input fields validated
rule of thumb
answer

you need to think

solve problems
be creative
break out of the mold
do the right thing

build a great product

don't be a script because this isn't scriptable

be a developer

develop

Smart People Not So Great at Murder Mystery Dinner Theater

We had a team celebration today at Kells and it was a rather fun murder mystery dinner (well, lunch) theater from Eddie May Mysteries.

The idea is that, while you eat, there are actors who come in portraying different suspects in a murder mystery. You talk to them, they leave "clues" and "evidence" at your table, and at the end you have to figure out who the killer was.

Our mystery took place supposedly in 1929, so all the actors had costumes on and acted like it was the roaring '20s.

At one point, one of the characters left some "evidence" at our table - a purse with a bloody handkerchief in it.

Now, I'm a fan of mystery movies and such, so the first thing I start thinking is, red herring. I mean, no way it's going to be this blatant, right?

Finally we got a chance to ask the characters questions, sort of near the end. The actress who had the bloody handkerchief sat down and I started thinking... given the time period... woman with a bloody handkerchief... maybe not living the healthiest lifestyle...

"Are you sick?" I asked.

"What?" The actress looked a little shocked.

"Sick. Are you sick? Like, not mentally ill, but physically. Do you have any disease?"

"Why do you ask?" She looked legitimately confused and started acting like I was being offensive to her character.

"Well, given the time frame, somewhat close to turn of the century, I'm wondering if you have tuberculosis. Consumption. You have a bloody handkerchief in your purse and there's no way it's that obvious."

At this point she actually broke character. "This is what you get when you do the show for smart people. I've been doing this show for years and I've literally never heard anyone ask that question. Have you ever heard of Occam's razor?"

I couldn't help but giggle a little. "Yeah."

"OK, good. Think way... way... way simpler than that. Truly, you're overthinking it." She laughed a little.

So... Maybe I should stop watching so many mystery movies. I always expect it to be this crazy convoluted thing now and I ignore the obvious stuff.

I ended up getting it right, despite over-thinking it.

Anyway, it was a pretty sweet event. The actors were good and were obviously having fun with it, which made it all the more fun for us. I would totally do it again.