Sponsored By Aspose - File Format APIs for .NET

Aspose are the market leader of .NET APIs for file business formats – natively work with DOCX, XLSX, PPT, PDF, MSG, MPP, images formats and many more!

ASP.NET MVC and Validation Using IDataErrorInfo and Validation Application Block

I have talked about various ways to use the Validation Application Block with the MVC Framework::



but in the new ASP.NET MVC Release Candidate we now have support for IDataErrorInfo.


I am mainly interested in how we can get this new validation mechanism working with a validation framework, such as the Validation Application Block, Castle.Validator, or NHibernate Validator as most developers use a validation framework if at all possible.


 


Validation Application Block


Taking the Validation Application Block for a spin via IDataErrorInfo can be done as follows. Add Validator Attributes to our Customer Class. Note the attributes are optional. You can also create a more POCO experience by adding the validators in a configuration file:


 



public partial class Customer


{


    public int Id { get; set; }


 


    [StringLengthValidator(1, 50,


        MessageTemplate = “Name must be between {3} and {5} chars.”)]


    public string Name { get; set; }


 


    [StringLengthValidator(1, 75,


        MessageTemplate = “Email must be between {3} and {5} chars.”)]


    [RegexValidator(@”\w+([-+.’]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*”,


        MessageTemplate = “Valid Email Required.”)]


    public string Email { get; set; }


}


 


 


Implement IDataErroInfo on the Customer Class.


 



public partial class Customer : IDataErrorInfo


{


    public string Error


    {


        get


        {


            return string.Empty;


        }


    }


 


    public string this[string columnName]


    {


        get


        {


            return DoValidation(columnName);


        }


    }


 


    protected virtual string DoValidation(string columnName)


    {


        var results = Validation.Validate<Customer>(this);


 


        foreach(var result in results)


            if (result.Key.Equals(columnName))


                return result.Message;


 


        return string.Empty;


    }


}


 


The code is pretty straightforward above. I am using the Validation facade class in the Validation Application Block. It would be more prudent to loosely couple it behind something like IValidator or IValidator<T> in the real-world.


One of the issues here is that Validation.Validate will be called for each property that is set, which can be incredibly expensive in terms of performance. The Validation Application Block does some internal caching which I believe will make this a somewhat inexpensive call each time in trivial scenarios, but obviously if you are making some expensive SelfValidation calls this could lead to disaster in terms of performance. When using any validation framework one needs to be careful here, because most use reflection and most will not allow you to only check validation on a per property basis. It is usually all or nothing when it comes to validation and thus validation is being fired over and over again for the entire entity.


When you implement this in such a scenario in your CustomersController:


 



[AcceptVerbs(HttpVerbs.Post)]


public ActionResult Create(FormCollection form)


{


    var customer = new Customer();


 


    try


    {


        UpdateModel<ICreateCustomerForm>(customer);


        // Do Something


 


        return RedirectToAction(“Index”);


    }


    catch (InvalidOperationException ex)


    {


        return View(customer);


    }


 


    // …


}


 


an InvalidOperationException will be thrown if you return anything but string.Empty from one of the IDataErrorInfo members.


If you are using the DefaultModelBinder, which is the case here, it automatically adds errors via ModelState.AddModelError in the background for you which is why when you re-display the form you will see the errors.


 


 


 


Conclusion


In general, I really like the option of using IDataErrorInfo as part of the validation strategy, but I still haven’t decided the best way to use it if at all. Initially it appears I have a lot less control over the validation which may mainly be of value in very trivial situations. Still contemplating it for the meantime. If anyone has any thoughts, it is always appreciated.


 


David Hayden


 

This entry was posted in Uncategorized. Bookmark the permalink. Follow any comments here with the RSS feed for this post.

13 Responses to ASP.NET MVC and Validation Using IDataErrorInfo and Validation Application Block

  1. David Hayden says:

    Thanks for the tip, bezieur. I will have to play with that this week.

  2. bezieur says:

    If you use NHibernate Validator you can use: ValidatePropertyValue method, and so:

    public string this[string columnName]
    {
    get
    {
    var results = Validator.ValidatePropertyValue(this, columnName);
    int numberOfErrors = results.Length;

    if (numberOfErrors == 0)
    {
    return string.Empty;
    }

    StringBuilder sb = new StringBuilder();
    for (int i = 0; i < numberOfErrors; i++)
    {
    sb.Append(results[i].Message);
    if (i < numberOfErrors – 1)
    {
    sb.Append(Environment.NewLine);
    }
    }

    return sb.ToString();
    }
    }

  3. David Hayden says:

    Daniel,

    Interesting… I’ll have to try it after the weekend. My belief, which I thought I actually tested, was that the model was not completely updated at that point. But it sounds like that may not be the case.

    If that is not the case, then certainly your technique of only calling Validate once is doable. However, unless you want to use IDataErrorInfo, I think the technique I mention at the link below is a cleaner technique overall when using something like the VAB:

    http://codebetter.com/blogs/david.hayden/archive/2009/02/03/an-aha-moment-on-mvc-validation-extensibility-in-defaultmodelbinder-bye-to-idataerrorinfo.aspx

    However, I still want to try out your idea since I may have been mistaken on how the binding occurred with IDataErrorInfo.

    Thanks for following up!

  4. David,

    Odd. I can’t think of a reason they’d design it that way.

    I ran a quick test with three fields and it returned error messages for all three, so that is a preliminary indicator that it might work.

  5. David Hayden says:

    Daniel,

    I don’t have time to check it, but I don’t believe that works.

    The model has not completely had all its properties set before you call validation. Therefore your validation results cached are based only on the first property being set.

  6. David Hayden says:

    Areg,

    Actually you have that backwards. UpdateModel uses TryUpdateModel. TryUpdateModel returns a bool and if not successful, UpdateModel will raise the InvalidOperationException. Therefore, one never has an exception thrown with TryUpdateModel.

  7. Areg Sarkissian says:

    Beware that TryUpdateModel and UpdateModel both use the same default binder as the model binders do.
    And internally TryUpdateModel still uses exceptions but catches and returns errors.

  8. Here’s an approach that doesn’t take the performance hit:

    private IDictionary validationResults;

    #region IDataErrorInfo Members

    public string Error
    {
    get { return null; }
    }

    public string this[string columnName]
    {
    get
    {
    Validate();

    string message = null;

    validationResults.TryGetValue(columnName, out message);

    return message;
    }
    }

    #endregion

    ///

    /// Use the Enterprise Library to perform field validation.
    ///

    ///
    /// Caches the results so that repeated calls don’t incur
    /// additional cost.
    ///

    private void Validate()
    {
    if (validationResults == null)
    {
    validationResults = new Dictionary();

    ValidationResults results = Validation.Validate(this);
    foreach (var result in results)
    {
    validationResults[result.Key] = result.Message;
    }
    }
    }

  9. Dom Finn says:

    This is a great idea thanks.

  10. David Hayden says:

    @Eric

    If you don’t like the exception, just use TryUpdateModel. This avoids the exception and just returns a bool as to whethere the binding was valid or invalid.

  11. Ryan Riley says:

    Great post, David. I had something similar in my notebook to post soon, but I was never able to get it working like I wanted. Overall, I like the Validation block, and I think the options of attributes or configuration files are very helpful for making a very flexible validation system. (Add a dash of Oslo, and you might even get your business experts involved!)

    As to your concern over multiple calls to Validate, I had the same concern and decided to add the Validate method to the property setters, similar to implementing INotifyPropertyChanged. You still have multiple calls, but in that case, you may also affect other property values (i.e. calculated properties), so I considered it not too big of a deal.

    The real problem I had with the above was having to repeat all of the above code for every class using the Validation block. I looked at using PostSharp to inject the property setter validaton invocations and the Validate method, but PostSharp seemed unable to reflect on the correct type. (That’s all probably related to my newness to AOP and PostSharp than anything.)

    @Eric: failed validation as exceptional cases really depends on your design. If you have designed your objects to remain in a consistent state, but user input invalidates that consistency, then you might very well say you have an exception. Given the above, I would think that exceptions might often be easier (and various frameworks such as Validation Aspects for PostSharp seem to think likewise.)

  12. Stephen says:

    Yea I wouldn’t recommend anybody use this style of validation, when we first considered validation requirements we were absolutely sure the built in bindings validation wasn’t right..

    We implemented a model where validators can be created (either stood off from a model, or by using an interface / meta data), the validators would take an enumerable of string ‘property’ names and their potential values and asked to return a validation result that contained a list of violations grouped by property, the violations detailed exactly why the data isn’t acceptable.

    Its a very minimal design and is easy for anybody to roll such a system in a couple of hours (if that).

  13. Eric says:

    Using exceptions to handle validation errors, really? I guess I wouldn’t consider data validation issues exceptional.

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>