3

Suppose you have a viewModel:

public class CreatePersonViewModel
{
    [Required]
    public bool HasDeliveryAddress {get;set;}

    // Should only be validated when HasDeliveryAddress is true
    [RequiredIf("HasDeliveryAddress", true)]
    public Address Address { get; set; }
}

And the model Address will look like this:

public class Address : IValidatableObject
{
    [Required]
    public string City { get; set; }
    [Required]        
    public string HouseNr { get; set; }
    [Required]
    public string CountryCode { get; set; }
    [Required]
    public string FirstName { get; set; }
    [Required]
    public string LastName { get; set; }
    [Required]
    public string ZipCode { get; set; }
    [Required]
    public string Street { get; set; }

    #region IValidatableObject Members

    public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
    {
        string[] requiredFields;
        var results = new List<ValidationResult>();

        // some custom validations here (I removed them to keep it simple)

        return results;
    }

    #endregion
}

Some would suggest to create a viewmodel for Address and add some custom logic there but I need an instance of Address to pass to my EditorTemplate for Address.

The main problem here is that the validation of Address is done before the validation of my PersonViewModel so I can't prevent it.

Note: the RequiredIfAttribute is a custom attribute which does just what I want for simple types.

Peter
  • 14,221
  • 15
  • 70
  • 110

2 Answers2

7

Would have been a piece of cake if you had used FluentValidation.NET instead of DataAnnotations or IValidatableObject which limit the validation power quite in complex scenarios:

public class CreatePersonViewModelValidator : AbstractValidator<CreatePersonViewModel>
{
    public CreatePersonViewModelValidator()
    {
        RuleFor(x => x.Address)
            .SetValidator(new AddressValidator())
            .When(x => x.HasDeliveryAddress);
    }
}
Darin Dimitrov
  • 1,023,142
  • 271
  • 3,287
  • 2,928
  • I had never heard of this library but so far I'm very impressed. Thanks! – Peter Nov 15 '11 at 13:55
  • 1
    @Peter, I am using this library since the first version of ASP.NET MVC 1. When I saw that Microsoft offer data annotations to perform validation I said to myself: WTF!!! Declarative validation is sooo much more limited compared to imperative validation. There are scenarios that simply are too difficult to express in a declarative manner using those attributes. I mean they work perfectly fine for stuff like Required, Min, Max, ... which is what the internet is flooded with, but once you start writing real world applications you realize their limitations. – Darin Dimitrov Nov 15 '11 at 14:02
  • So simply `Install-Package FluentValidation.MVC3`, add the following line to your `Application_Start`: `FluentValidationModelValidatorProvider.Configure();`, get rid of all the data annotation crap and start writing real validators for your view models. An additional benefit of this is that the validation logic is separate from the view model and could be [unit tested](http://fluentvalidation.codeplex.com/wikipage?title=Testing&referringTitle=Documentation) in isolation. You also have the possibility to group rules and apply them conditionally based on HTTP parameters, ... – Darin Dimitrov Nov 15 '11 at 14:04
  • I see it integrates perfectly with the regular DataAnnotations. So, for now, to keep the learning curve for my colleagues a little lower I'm going to use FluentValidation only for complex validations and keep the stupid ones like Required for DataAnnotations. – Peter Nov 15 '11 at 14:19
0

Simon Ince has an alpha release of Mvc.ValidationToolkit which seems to be able to do what you want.

Update
As I understand it, the 'problem' lies in the DefaultModelBinder class, which validates your model on the basis that if it finds a validation attribute it asks it if the value is valid (quite reasonable really!), it has no notion of hierarchy. In order to support your required functionality you'll have to write a custom model binder that binds and then validates, if required, as determined by your declarative markup.
If you do write such a class it may be a good candidate for MVC futures.

Rich O'Kelly
  • 41,274
  • 9
  • 83
  • 114
  • Sadly enough his RequiredIfAttribute only works for simple types, not for complex types such as my Address. I am using his RequiredIfAttribute already. – Peter Nov 15 '11 at 13:07
  • @Peter Updated with suggested solution, unfortunately nothing like this already exists as far as I can tell so you'll be breaking new ground! – Rich O'Kelly Nov 15 '11 at 15:31
  • I'd say that your observation is correct, unfortunately I have no time to create a custom ModelBinder with this sort of functionality, let alone test is thoroughly. For now FluentValidation seems to be a better alternative. I would realy like to see such a thing implemented in future versions of MVC though. Because right now I feel MVC is lacking when it comes to real world applications. – Peter Nov 15 '11 at 15:32