Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Validating Nested Models

I currently have a ViewModel setup as such:

public class OurViewModel
    {
        public OurViewModel() { }

        [Required]
        public int LeadID { get; set; }
        [Required]
        public int Rate { get; set; }
        [Required]
        public bool DepositRequired { get; set; }
        [RequiredIfOtherPropertyIsTrue("DepositRequired")]
        public BankInfo { get; set; }
    }

...in this case, "RequiredIfOtherPropertyIsTrue" is a validator that does pretty much what it says: checks to see if another property is true (in this case, our boolean indicating whether or not a deposit is required), and BankInfo is another model that looks something like this:

public class BankInfo
{
    public enum AccountTypeEnum
    {
        CHECKING,
        SAVINGS
    }

    public BankAccountInfo() { }

    [DisplayName("Account Number")]
    [Required(ErrorMessage = "You must provide a valid bank account number")]
    public String AccountNumber { get; set; }

    [DisplayName("Bank Routing Number")]
    [Required(ErrorMessage = "You must provide a valid routing number")]
    [StringLength(9, MinimumLength = 9, ErrorMessage = "Your bank routing number must be exactly 9 digits")]
    public String ABANumber { get; set; }

    [DisplayName("Bank Account Type")]
    [Required]
    public AccountTypeEnum AccountType { get; set; }

    [DisplayName("Name on Bank Account")]
    [Required(ErrorMessage = "You must provide the name on your bank account")]
    public String AccountName { get; set; }
}

Now, in our ViewModel, we have a checkbox bound to our DepositRequired boolean, and an EditorFor w/ a custom template for BankInfo. Upon submission, we're having trouble figuring out how disable validation on BankInfo IF it's not required by the model (eg. even if we don't require the property on the ViewModel, it's still triggering val on BankInfo and therefore, failing miserably on any form post). Is there any standard way for dealing w/ nested model validation on ViewModel bind?

like image 548
antinescience Avatar asked Dec 26 '12 16:12

antinescience


1 Answers

Unfortunately, with the built in validation, you'd have to use

ModelState.Remove("BankInfo");

to conditionally ignore any validation failures on that object.

If using FluentValidation is an option, you can do something like this in your OurViewModelValidator:

RuleFor(ourViewModel=> ourViewModel.BankInfo).SetValidator(new BankInfoValidator()).When(ourViewModel=> ourViewModel.DepositRequired);

and then let the BankInfoValidator handle validation of that object.

Something like:

public class BankInfoValidator : AbstractValidator<BankInfo>
{
    public BankAccountInfoValidator() 
    {
        RuleFor(bank => bank.AccountName).NotEmpty().WithMessage("You must provide a name for your bank account.");
        RuleFor(bank => bank.AccountNumber).NotEmpty().WithMessage("You must provide an account number for your bank information.");
        RuleFor(bank => bank.AccountType).NotEmpty().WithMessage("You must select what kind of bank account you're entering information for (checking, savings).");
        RuleFor(bank => bank.ABANumber).NotEmpty().WithMessage("You must provide a routing number for your bank information.");
        RuleFor(bank => bank.ABANumber).Must(BeOnlyDigits).WithMessage("Your routing number can only contain numeric characters, 0-9.");
        RuleFor(bank => bank.AccountNumber).Must(BeOnlyDigits).WithMessage("Your account number can only contain numeric characters, 0-9.");
    }

    private bool BeOnlyDigits(string digitString)
    {
        int result;
        return int.TryParse(digitString, out result);
    }
like image 181
Justin Edwards Avatar answered Sep 25 '22 15:09

Justin Edwards