dotnet, gists, aspnet, csharp comments edit

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 theDefaultModelBinder.ResourceClassKeystatic 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.

windows, gists comments edit

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.

General Ramblings comments edit

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!

dotnet, gists, testing comments edit

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 ArgumentNullExceptionas an example. Say you add some more validation to the GetDatamethod 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.

personal comments edit

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