Tuesday, November 2, 2010

Adding an item to the ValidationSummary programmatically

In ASP.NET, the ValidationSummary can be used to display errors to the end user. They can specific to a field or just be entity specific, etc. In general, field specific validation shows up next to a control that it is validating (assuming you put the validators next to it). But what about validation that happens in a Domain Service Class or your Custom BLL for example? These exceptions will by default be caught by the application and show as a nasty error to the user, or go to the error page. This is hardly the desired behavior for a validation error.

First I like to change the default behavior of bubbling up to the application to be caught to being handled at the button or page level. To do this I put a try-catch in my button action or other applicable event that you can tap into. In the catch, it would be ideal to add a custom error message to the ValidationSummary. How do we do that though?

Thankfully, it is quite easy to add an item to the ValidationSummary. The key is that the Page has a Validators property that all validators are automatically added to when you put them on your .aspx page. The problem is that we don’t have a CustomValidator.

Thus we need to create a CustomValidator, but what a pain really since we only want to use it when we actually have an exception in our BLL. My solution is to create method to encapsulate the logic to create a new CustomValidator and add it to the Page’s Validators collection. So that it can easily be accessed on any page, I have implemented it as an Extension to the Page class. Below is the code to do so.

namespace MyExtensions
{
    public static class PageExtensions
    {
        public static void AddValidationSummaryItem(this Page page, string errorMessage)
        {
            var validator = new CustomValidator();
            validator.IsValid = false;
            validator.ErrorMessage = errorMessage;
            page.Validators.Add(validator);
        }
    }
}

To use this method just put the using MyExtensions; statement at the top of your code-behind of the page that you want to use it on. Then you can do the following:

protected void btnSubmit_Click(object sender, EventArgs e)
{
    try
    {
        // do some stuff like call my BLL that may throw an exception
    }
    catch (Exception ex)
    {
        if (ex.Message == "Some key string I want to handle")
        {
            Page.AddValidationSummaryItem("Password must be at least 6 characters in length.");
        }
        else
        {
            throw ex;
        }
    }
}

WARNING: Be careful with what you display to the end user. You should never catch an exception and display the Message directly to the user. It could have information that a hacker can use to compromise your application.

UPDATE 5/5/2011:

You may notice that there are times the above method does not work as expected. In particular you may notice that the error message does not show in the ValidationSummary control as you expected even though the Page.IsValid is false (as expected). The problem is likely that you have specified the ValidationGroup on the ValidationSummary control. This is a common issue when using validators and ValiationSummary controls. They must have the same value for ValidationGroup in order to tie them together. When using DynamicData templates the ValidationGroup is NOT set so the above method works great. However, in other situations such as a simple form or maybe the FormView the ValidationGroup property may be specified depending on how your code was generated or if you changed the defaults.

To help with that issue, I have created another version (overloaded method) of this method that takes one addition parameter called validationGroup.

public static void AddValidationSummaryItem(this Page page, string errorMessage, string validationGroup)
{
    var validator = new CustomValidator();
    validator.IsValid = false;
    validator.ErrorMessage = errorMessage;
    validator.ValidationGroup = validationGroup;
    page.Validators.Add(validator);
}

WARNING: If you call Page.Validate() or Page.Validate(“your validation group here”) after the above call, the validator will be reset to valid since we hardcoded the IsValid value and the default is valid.

4 comments:

Ramon Araujo said...

Cool! Both the use of standard validation mechanism and an elegant use of Extension Methods too.

Jan said...

Clean and simple. Thank you.

smithani said...

Wow, thank you sooo much for this post.

Anonymous said...

great post!