Archive

Archive for February, 2011

Putting your model layer in charge of validation

February 27th, 2011 1 comment

This title is directly taken for a section in Steve Sanderson’s book on Asp.Net MVC2 (2nd Edition). I have been struggling with using MVVM with model binding validations built in vs a domain model that binds up to the ModelState. I’m thinking that there is room for there both but using both approaches may be confusing.

When thinking about validation, he suggests that there are three ways in which the first two are build into MVC (p.450):

  • Making demands about the presence or format of data that users may enter into a UI
  • Determining whether a certain .NET object is in a state that you consider valid
  • Applying business rules to allow or prevent certain operations being carried out against your domain model

And I’m like a lot of people I worry about and begin with the third item first. That’s why I build out my domain first and then worry about the UI (that’s not to say that I don’t build out my domain while thinking about the UI!).

So, model validations in the model binding are really easy. There’s no wiring needed, they really do just work. For scaffolding, they’re great. They feel just like my first days on Rails. But to be honest, I suspect that as I craft my views and particularly head towards a fat-client (aka rest-based, jQuery-based client) that all this binding won’t be needed. But my domain still needs to be rock solid. Something still felt wrong from my experience. Luckily, Sanderson gets to it and the end of the chapter. On p.472 he explains that we need to put the model layer in charge because all this binding is not an ideal separation of concerns and leads to practical problems of repetition, obscurity, restriction of technology choices (phew, can I use Castle?) and my favourite, an “unnatural chasm between validation rules and business rules”. He reckons that dropping in a [Required] on a model is convenient for the UI but in practice there are business rules in behind that get more complex.

Of course, the solution isn’t hard and it was just as I have already done. It requires:

  • a list of errors (which usually involve rule information where (properties) and what (the values) that were a problem)
  • often wrapping those in an exception
  • a handler that binds those to the ModelState (because it is the mvc specific part)

Thanks Steve. I was wondering how I was going to explain this to the group I am about to teach next week. Here’s some code that I use (and I think some of this was from a colleague Mark originally).

Here’s our errors and summary of errors:

  public struct Error
  {
      public string Key { get; set; }
      public string Message { get; set; }

      public Error(string key, string message) : this()
      {
          Key = key;
          Message = message;
      }
  }
  public class ErrorSummary
  {
      public static ErrorSummary Empty
      {
          get { return new ErrorSummary(new List<Error>()); }
      }

      public List<Error> Errors { get; private set; }

      public ErrorSummary(List<Error> errors)
      {
          Errors = errors;
      }

      public ErrorSummary(Error error) :
          this(new List<Error> {error })
      {
      }
      public ErrorSummary(string key, string message) :
          this( new Error(key, message))
      {
      }

      public ErrorSummary AddError(string key, string message)
      {
          Errors.Add(new Error(key, message));
          return this;
      }
  }

Then we’ll wrap them in an exception to caught in the mvc framework:

  public class BusinessRuleException : Exception
   {
       public BusinessRuleException(ErrorSummary summary)
       {
           Summary = summary;
       }

       public BusinessRuleException(string key, string message)
       {
           Summary = new ErrorSummary(key, message);
       }

       public BusinessRuleException(string message)
       {
           Summary = new ErrorSummary("", message);
       }

       public virtual ErrorSummary Summary { get; private set; }
   }

Now we need some code that with gather the errors from our validation framework (eg Castle in this case). We’ll construct them as extension methods for fluent syntax.

  /// <summary>
  /// Validation extensions to wrap around PI models using the Castle Validations
  /// </summary>
  public static class ValidationExtension
  {
      private static readonly CachedValidationRegistry Registry = new CachedValidationRegistry();

      public static bool IsValid<T>(this T model) where T : class 
      {
          return new ValidatorRunner(Registry).IsValid(model);
      }

      public static ErrorSummary GetErrors<T>(this T model) where T : class
      {
          var runner = new ValidatorRunner(Registry);
          return !runner.IsValid(model) ? ToErrorSummary(runner, model) : ErrorSummary.Empty;
      }

      public static void ThrowValidationSummary<T>(this T model) where T : class
      {
          var runner = new ValidatorRunner(Registry);
          if (!runner.IsValid(model))
          {
              throw new BusinessRuleException(ToErrorSummary(runner, model));
          }
      }

      private static ErrorSummary ToErrorSummary<T>(IValidatorRunner runner, T model)
      {
          return new ErrorSummary(ToErrors(runner, model));
      }

      private static List<Error> ToErrors<T>(IValidatorRunner runner, T model)
      {
          var errors = new List<Error>();
          var errorSummary = runner.GetErrorSummary(model);
          for (var i = 0; i < errorSummary.ErrorsCount; i++)
          {
              errors.Add(new Error(errorSummary.InvalidProperties[i], errorSummary.ErrorMessages[i]));
          }
          return errors;
      }
  }

Now for MVC controller code. Here we check that a model is valid and throw summary if it isn’t valid. We catch the exception and then bind it in the ModelState.

  public ActionResult SomeAction(Model model)
   {    
        try  // very naive implementation
        {
           model.ThrowValidationSummary();
        }
        catch (Exception ex)
        {
            if (ErrorHandling != null)
                ErrorHandling(ex, ModelState);
            else
                throw;
        }

       // rest of implementation
   }

   protected Action<Exception, ModelStateDictionary> ErrorHandling = (ex, m) =>
   {
       if (typeof(BusinessRuleException) == ex.GetType())
       {
           ((BusinessRuleException)ex).Summary.Errors.ForEach(x => m.AddModelError(x.Key, x.Message));
       }
       else if (typeof(ArgumentException) == ex.GetType())
       {
           m.AddModelError(((ArgumentException)ex).ParamName, ex.Message);
       }
       else if (typeof(UnauthorizedAccessException) == ex.GetType())
       {
           m.AddModelError("Authorization", "Authorisation Denied");
       }
       else if (typeof(HttpRequestValidationException) == ex.GetType())
       {
           m.AddModelError("Validation", "Error");
       }
   };

In practice, this code would be split up. The model checking lives in the service/repository doing the work. I would have the exception handling centralised (eg base class). The list of exception handlers really should be registered a little more nicely than this too.

Go and have a read of Sanderson. You’ll see a similar implementation. A final point by him. This doesn’t mean the death of client-side validation.

Just because your model layer enforces its own rules doesn’t mean you have to stop using ASP.NET MVC’s built-in validation support. I find it helpful to think of ASP.NET MVC’s validation mechanism as a useful first line of defense that is especially good at generating a client-side validation script with virtually no work. It fits in neatly with the view model pattern (i.e., having simple view-specific models that exist only to transfer data between controllers and views and do not hold business logic): each view model class can use Data Annotations attributes to configure client-side validation.
But still, your domain layer shouldn’t trust your UI layer to enforce business rules. The real enforcement code has to go into the domain using some technique like the one you’ve just seen. (p.476)

So now I’m heading off to look at knockout.js – I’m hoping for the it to help me with client-side validation.

Categories: Uncategorized Tags:

UrlBinder for model properties using Url as a type

February 27th, 2011 1 comment

I had the simple problem that my Url properties wouldn’t bind when return back into an action on a controller (in asp.net mvc 2). Take the model below, I want to have a destination as an Url. Seems fair?

  public class Banner
  {
      public string Name { get; set; }
      public Url Destination { get; set; }
  }

Looking at the controller code below, when I enter the action where I had done a form post to I just couldn’t see a value in the banner.Destination but I could see one in banner.Name.

  public ActionResult Create(Banner banner)
   {
       // banner had no Destination value but did have Name  
       // ... rest of code
   }

Wow, I thought. No Url. HHhhhhmmmm. But I really want to be strongly typed – all my unit and integration tests worked and I felt that I wanted my domain model to remain, well, true. Given how long it took me to research the solution one suspects I should have just made the Url and string. But I didn’t and I’m glad. The solutions is simple and I understand the pipeline much better.

I need to implement IModelBinder for the model Url (which is used by the property). While Url isn’t a custom class for our domain it is treated as one by mvc. Here is my UrlModelBinder implementation and hooking it up.

  public class UrlBinder : IModelBinder
  {
      public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
      {
          if (!String.IsNullOrEmpty(bindingContext.ModelName))
          {
              var name = bindingContext.ModelName;
              var rawvalue = bindingContext.ValueProvider.GetValue(name).AttemptedValue;
              var value = new ValueProviderResult(rawvalue, rawvalue, CultureInfo.CurrentCulture);
              bindingContext.ModelState.SetModelValue(name, value);
              return new Url(rawvalue);  // I'm unclear that this needs to be returned
          }
          return null;
      }
  }

And then ensuring that it resolves against the Url.

  protected override void  Application_Start()
  { 
      ModelBinders.Binders.Add(typeof(Url), new UrlBinder());
  }

So, the key to this solution is understanding – and this is quite obvious now that I understand the implementation in DefaultModelBinder – is that Url is a model and not a property. I kept thinking that I needed bind the form value to the property. Well I do but for a “custom” type. Url is custom because (as the documentation clearly states) it is does not inherit from string. Only string properties are bind by default – that includes enumerations of string (eg IList). So once you know that, it is straightforward that the solution is to implement your own IModelBinder for Url. (If you are interested use Reflector or the source on CodePlex and look through the DefaultModelBinder – you’ll see the way they implement BindSimpleModel and BindComplexModel)

This binder is pretty naive so I expect as I learn more it will be improved. Something I didn’t expect was that I don’t really need to construct an Url because in the model binding at this stage I just need be able to have a binder for the type rather than worry about constructing the type. I do this by putting rawvalue into the ValueProviderResult – it only accepts strings. This code then gets picked up again in the DefaultModelBinder when it constructs the model (in this case Banner) which it then easily creates a new Url(value).

A simple question, why isn’t there a binder for Url already in the code? Is not Url effectively primitive for the web? Clearly not.

If you’ve got this far and are interested there are other ways that you really don’t want to use to solve this problem:

  • use an action filter ([Bind(exclude="Destination")]) and then read it from the Request.Form
  • implement my own IValueProvider on the property banner
  • rewrite the ModelState explicitly clearing errors first
  • implement an IModelBinder for the Banner class
  • and the biggest and most heavy-handed of all, subclass DefaultModelBinder

Luckily, none of these solutions are needed. None will feel right because they are either too specific to the class or property, too kludgy because they use Request.Form and just way too big for a small problem.

Categories: Uncategorized Tags: