Dependency Injection in ASP.NET MVC 2 – Part 3: Custom DataAnnotation ValidationAttributes

08 March 2010 - 11:55 PM / by Dominic Pettifer. 9 Comments

Technical Article - Part 3 in a series on Dependency Injection in ASP.NET MVC shows how to inject dependencies into custom ValidationAttributes, used to implement validation using the DataAnnotations framework. Have you ever needed to hit a database when validating a ViewModel/form submit, and wondered how to do it?

System.ComponentModel.DataAnnotations

In part 1 we covered configuring and using an IOC Container for injecting dependencies into Controllers, and the benefits of this approach, please consult that article for an overview. We're using Castle Windsor as the IOC Container.

One of the major changes introduced in ASP.NET MVC 2.0 over 1.0 was vastly improved support for form validation, that is validating required fields, valid email addresses etc. when a user submits a form in your web app. Microsoft providing for this in the way of support for DataAnnotations on your ViewModels. Consider this Customer Model:

public class CustomerForm
{
    [Required]
    [StringLength(50)]
    public string Name { get; set; }

    [Required]
    [RegularExpression(Regexes.EmailAddress)]
    public string EmailAddress { get; set; }

    [RegularExpression(Regexes.PhoneNumber)]
    public string PhoneNumber { get; set; }
}

Notice the Required, StringLength, and RegularExpression attributes on the properties? These are part of the DataAnnotations library, and by default ASP.NET MVC 2.0 will read these and provide validation to our input forms, so in the Controller POST method we write:

[HttpPost]
public ActionResult Add(CustomerForm form)
{
    if (!ModelState.IsValid)
    {
        return View(form);
    }

    Customer customer = new Customer();
    customer.Name = form.Name;

    // ...fill out other properties and persist to database (snip)... // 

    return RedirectToAction("Edit"); 
}

Custom ValidationAttributes

There are a limited range of ValidationAttributes in the DataAnnotations library. Fortunately you can create your own quite easily by extending the class System.ComponentModel.DataAnnotations.ValidationAttribute and overriding the IsValid method:

public class MyCustomValidationAttribute : ValidationAttribute
{
    public override bool IsValid(object value)
    {
        // Do validation logic
        return true;
    }
}

public class CustomerForm
{
    [MyCustomValidation]
    public string Name { get; set; }

    // ...other properties (snip)... //
}

Validation that Needs to Hit a Database

Often we need to perform validation that calls some external dependency such as hitting a database to check Email Addresses are unique. If we are keeping our codebase decoupled, and adhering to DIP (Dependency Injection Principle), then this can be tricky as we need to find some way of injecting our Repository into our custom ValidationAttribute.

It's said that we should try to favour constructor injection when doing IOC, but this isn't possible with .NET Attributes because of the unique way these are instantiated, and we'd end up with code like this:

public class CustomerForm
{
    [UniqueEmail(IocHelper.Container().Resolve<ICustomerRepository>())]
    public string EmailAddress { get; set; }

    // ...other properties (snip)... //
}

...which just looks plain ugly, and is also an IOC Anti-Pattern because we now have a dependency on our Dependency Injection Container (oh the irony!). So instead we have to rely on Property Injection, note the following implementation of our UniqueEmail validation code:

public class UniqueEmailAttribute : ValidationAttribute
{
    public ICustomerRepository CustomerRepository { get; set; }

    public override bool IsValid(object value)
    {
        return CustomerRepository.FindByEmail(value ?? "") == null;
    }
}

Now the million dollar question, how do we inject an ICustomerRepository into this object? We need to provide a custom DataAnnotationsModelValidator class, replacing the default one.

using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Reflection;
using System.Web.Mvc;
using MvcLibrary.Ioc;

namespace MyLibrary.IocUtils
{
    public class IocValidator : DataAnnotationsModelValidator<ValidationAttribute>
    {
        public IocValidator(ModelMetadata metadata, ControllerContext context,
            ValidationAttribute attribute) : base(metadata, context, attribute) { }

        public override IEnumerable<ModelValidationResult> Validate(object container)
        {
            IList<PropertyInfo> props = (from p in Attribute.GetType().GetProperties()
                                         where p.CanRead && p.CanWrite
                                             && (p.PropertyType.IsInterface || p.PropertyType.IsAbstract)
                                         select p).ToList();

            foreach (PropertyInfo prop in props)
            {
                if (IocHelper.Container().Kernel.HasComponent(prop.PropertyType))
                {
                    prop.SetValue(Attribute, IocHelper.Container().Resolve(prop.PropertyType), null);
                }
            }

            return base.Validate(container);
        }
    }
}

We basically hook into the point just before the IsValid method is called by the DataAnnotationsModelValidator type, and inject in whatever our dependency is. At this point we already have a fully instantiated ValidationAttribute object, so we're not going to get the IOC Container to instantiate it and automatically hook up the dependencies, and there isn't a way to get the IOC Container to inject property dependencies into a 'live' object. So instead we have to do things manually. First by looping through any of the ValidationAttribute's Properties that are either Interface and Abstract types, and that have both a getter and a setter defined. Then we loop through each property, checking the IOC Container to see if the Property's type has been registered, and if so resolve the dependency and set the Property. We then pass execution onto the base type's Validate implementation, where the logic to call the IsValid method is contained. If it can't find any dependencies, it'll just continue as normal.

Please check part 1 for the implementation for the IocHelper class, this class acts as a static helper gateway to the Castle Windsor IOC Container.

Last step is to register this DataAnnotationsModelValidator type in the Global.asax.cs:

protected void Application_Start()
{
    RegisterRoutes(RouteTable.Routes);
    // ...register other dependencies (snip)... //

    DataAnnotationsModelValidatorProvider.RegisterDefaultAdapter(typeof(IocValidator));
}

We completely replace the default DataAnnotationsModelValidator implementation with our own one, but since we're inheriting from DataAnnotationsModelValidator, all of the original behaviour and logic is left intact.

9 Comments on "IOC in MVC – Validation"

Post a Comment
  • Re:

    Set your life time easier take the <a href="http://bestfinance-blog.com">loan</a> and all you need.

    Posted on 6 September 2010 - 12:52 PM / by PateLAURIE

  • RE: IOC in MVC – Validation

    Maybe it's only me, but I'm not too crazy about the way you convert say a ProductForm to a Product in your action method (though I do this as well at the time).
    What would be the best way to simplify this? (At the moment I'm thinking about a helper class that does this, but figured let me ask first)

    Product product = new Product()
                {
                    Name = productForm.Name,
                    Description = productForm.Description,
                    Price = Decimal.Parse(productForm.Price),
                    Quantity = Int32.Parse(productForm.Quantity),
                    ParentCategory = CategoryRepository.GetCategoryFromId(productForm.SelectedCategoryId.Value)
                };

    Posted on 3 May 2010 - 7:55 AM / by Krok

  • RE: IOC in MVC – Validation

    Excellent series!

    The only problem I have is that when I combine the techniques of Part 2 and part 3 into a sample program they do not work together.

    I implemented part 3 first and have custom attribute validation working perfectly (thanks!)

    When I added part 2, the validation fails because the container managed dependency is not injected into my custom attribute. The dependency is null at runtime. If I take the smart model binding out the validation works again.

    I have tried various orderings in Global.asax.cs but it does not seem to make a difference.

    Any ideas?

    Posted on 24 March 2010 - 5:34 AM / by Mark

    • Source Code

      I've included the source code to a sample Visual Studio project that incorporates all three parts in this series, everything seems to work OK. Give it a try and see if it helps. Cheers!

      Dependency Injection Sample Project

      Posted on 5 April 2010 - 7:27 PM / by Dominic Pettifer (Administrator)

      • RE: Source Code

        Hi Dominic,

        Thanks for responding. I should have updated my previous post. My original diagnosis was not correct. The problem was not when I combined techniques #2 and #3, the problem was when I added custom client validation.

        I used this article as a guide:
        asp.net MVC custom validation

        After this line:
        DataAnnotationsModelValidatorProvider.RegisterDefaultAdapter(typeof(IocValidator));

        I have this line in my code:
        DataAnnotationsModelValidatorProvider.RegisterAdapter(typeof(StateNameAttribute), typeof(StateNameValidator));

        This is what causes the code to break because the dependencies of StateNameValidator are not injected. If I comment out that line then all is well.

        I still have not found a solution (though I have not had much time to try lately due to other obligations).

        Thanks for any assistance you can offer. Having the custom attribute validation work through an ajax call on client will be ideal for me and I hope I can make it work.

        Posted on 10 May 2010 - 9:02 PM / by Mark

        • RE: RE: Source Code

          I did eventually get it all working. There is no need to register a specific adapter, the default adapter can handle custom client validations just fine if the correct methods are overridden.

          Many thanks for these three articles!

          Posted on 16 June 2010 - 8:03 PM / by Mark

  • RE: IOC in MVC – Validation

    Hi there,

    This is good stuff and I have used a variant of it myself. Still new to MVC and the lack of built in IOC support is slightly frustrating but I am working through it now.

    One question I had was how do you combine the database check attribute with checking the email address is the correct format. I have 2 Attributes at the moment (one to check for RegEx and another to check email is used in the database).

    I then have had to put this odd code in the IsValid of the EmailInUseAttribute

    public override bool IsValid(object value)
            {
                //If not an email address then in use validation should not be fired
                if (!toptable.Util.Validation.IsEmailAddress(value.ToString()))
                    return true;
    
    //...Go and check in the database and return based on that result
    }

    This works but seems a little cumbersome? Any thoughts?

    Posted on 23 March 2010 - 7:37 PM / by Andrew Metcalfe

Leave a Comment

In Reply to comment by "Andrew Metcalfe"

Hi there,

This is good stuff and I have used a variant of it myself. Still new to MVC and the lack of built in IOC support is slightly frustrating but I am working through it now.

One question I had was how do you combine the database check attribute with checking the email address is the correct format. I have 2 Attributes at the moment (one to check for RegEx and another to check email is used in the database).

I then have had to put this odd code in the IsValid of the EmailInUseAttribute

public override bool IsValid(object value)
        {
            //If not an email address then in use validation should not be fired
            if (!toptable.Util.Validation.IsEmailAddress(value.ToString()))
                return true;

//...Go and check in the database and return based on that result
}

This works but seems a little cumbersome? Any thoughts?

Comment Details
*
* BBCode: [b]bold[/b], [i]italics[/i], [code]code[/code], [li]bullet point[/li], [h]Heading[/h], [url="http://www.example.com"]link[/url], [quote author="John Smith"]quote[/quote]

Random Image

James Bond pointing a gun.

Bond reveals a more human side to his character. (from the blog It’s Bond, But Not as You Know it )

Quick Poll

What is your DIP/IOC Container of choice?

Poll Vote
(see results)
View Comments (4) (See previous polls)

Latest Tweets

View Dominic Pettifer's Twitter page.