Home > Uncategorized > Putting your model layer in charge of validation

Putting your model layer in charge of validation

February 27th, 2011 Leave a comment Go to comments

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:
  1. James Barrow
    June 10th, 2011 at 03:11 | #1

    Very nice blog post.

    I’m interested in your Error struct, what is the purpose of the key property? If the service layer generates these, then it could add any arbitrary key that may or may not fit in with the UI’s ModelState properties and so m.AddModelError(x.Key, x.Message) may not tie into a UI component.

    A problem I’ve wondered about for a while is how to keep the validation of the domain in sync with the UI – e.g. maybe a DataContract has a FirstName property, but the UI’s ViewModel just has a FullName property, but errors for the FirstName property should show up by the UI component/field that is bound to the ViewModel’s FullName property.

    Or perhaps some complex business rule on the server side that has nothing to do with the service contract, but results in an error that is conceptually related to a UI field, and so a user would like a nice error message next to that field but to do so becomes complicated.

    How have you handled this?

    Hope I made sense :)

    Thanks,

    James

  1. No trackbacks yet.