1

I am trying to figure out why my FluentValidation is not unobtrusively validating some of my View Model properties.

I installed, via NuGet, the FluentValidation package for MVC3:

  Install-Package FluentValidation.MVC3

Results:

<package id="FluentValidation" version="5.0.0.1" targetFramework="net45" />
<package id="FluentValidation.MVC3" version="5.0.0.1" targetFramework="net45" />

The properties that are not being validated are some members of a Guitar class object, declared in my View model. The Guitar.ProductionYear property is getting the unobtrusive validation code but the other 2 properties of the Guitar object are not.

public Guitar Guitar1 { get; set; }
public Guitar Guitar2 { get; set; }
public Guitar Guitar3 { get; set; }

I read the FluentValidation docs, and now specifically about the limitations regarding FluentValidation's client-side validation of lists but this is not a list so I am hoping someone can see what's wrong and/or offer some suggestions.

I also tried using a custom Guitar object validator but still get the same results

Observations

  • Client-side validation is being generated and fires for properties FirstName,LasteName,Phone and Email

  • I did notice that ModelState is not valid for these objects when they are not filled in by the user but I don't know why the client-side validation is not being generated and subsequently firing.

  • The generated form HTML for the Guitar objects are missing the unobtrusive validation attributes. The other properties like FirstName, LastName, Email and Phone are validating fine
  • Example

        <!-- Unobtrusive JS generated-->
            <input type="text" value="" name="FirstName" id="FirstName" data-val-required="'First Name' must not be empty." data-val="true" class="input-validation-error">
            <div class="messageBottom"> 
                    <span data-valmsg-replace="true" data-valmsg-for="FirstName" class="field-validation-error"><span for="FirstName" generated="true" class="">'First Name' must not be empty.</span>
       </span>
     </div>
    
        <!-- Unobtrusive JS Partially generated for Guitar object-->
       <input type="text" value="" name="Guitar.Make" id="Guitar_Make" placeholder="Make">
            <div class="messageBottom">
            <span data-valmsg-replace="true" data-valmsg-for="Guitar1.Make" class="field-validation-valid"></span>
            </div>
    
            <input type="text" value="" name="Guitar1.Model" id="Guitar1_Model" placeholder="Model">
            <div class="messageBottom">
                <span data-valmsg-replace="true" data-valmsg-for="Guitar1.Model" class="field-validation-valid"></span>
            </div>
    
            <input type="text" value="" name="Guitar1.ProductionYear" id="Guitar1_ProductionYear" data-val-number="The field ProductionYear must be a number." data-val="true" placeholder="Production Year" class="valid">
            <div class="messageBottom">
            <span data-valmsg-replace="true" data-valmsg-for="Guitar1.ProductionYear" class="field-validation-valid">
            </span>
            </div>
    

    Thanks

    Global.asax.cs

    FluentValidationModelValidatorProvider.Configure();
    

    The View Model

    [FluentValidation.Attributes.Validator(typeof(CustomerViewModelValidator))]
    public class CustomerViewModel
    {
        [Display(Name = "First Name")]
        public string FirstName { get; set; }
    
        [Display(Name = "Last Name")]
        public string LastName { get; set; }
    
        [Display(Name = "Phone")]
        public string Phone { get; set; }
    
        [Display(Name = "Email")]
        public string EmailAddress { get; set; }
    
        [Display(Name = "Guitar 1")]
        public Guitar Guitar1 { get; set; }
    
        [Display(Name = "Guitar 2")]
        public Guitar Guitar2 { get; set; }
    
        [Display(Name = "Guitar 3")]
        public Guitar Guitar3 { get; set; }
    }
    

    The Custom Guitar Object Validator

    public class GuitarValidator : AbstractValidator<Guitar>
    {
        public GuitarValidator()
        {
            RuleFor(x => x.Make).NotEmpty();
            RuleFor(x => x.Model).NotEmpty();
            RuleFor(x => x.ProductionYear).NotEmpty();
        }
    }
    

    The View Model Validator

    public class CustomerViewModelValidator : AbstractValidator<CustomerViewModel>
    {
        public CustomerViewModelValidator()
        {
            RuleFor(x => x.FirstName).NotNull();
            RuleFor(x => x.LastName).NotNull();
            RuleFor(x => x.Phone).NotNull();
            RuleFor(x => x.EmailAddress).NotNull();
    
            //1st Guitar Object is Required
            RuleFor(x => x.Guitar).SetValidator(new GuitarValidator());
    
    
    
        }
    }
    

    The View

    <!-- Guitar Object #1 -->
         <div id="cosponsorsTemplate_1">
                <div class="formColumn1">@Html.LabelFor(x=>x.Guitar1)</div>
                <div class="formColumn2">@Html.TextBoxFor(x => x.Guitar1.Make, new { Placeholder = "Make" })
                    <div class="messageBottom">@Html.ValidationMessageFor(x => x.Guitar1.Make)</div>
                </div>
                <div class="formColumn3">@Html.TextBoxFor(x => x.Guitar1.Model, new { Placeholder = "Model" })
                    <div class="messageBottom">@Html.ValidationMessageFor(x => x.Guitar1.Model)</div>
                </div>
                <div class="formColumn4">@Html.TextBoxFor(x =>x.Guitar1.ProductionYear, new { Placeholder = "Production Year" })
                    <div class="messageBottom">@Html.ValidationMessageFor(x => x.Guitar1.ProductionYear)</div>
                    <a class="icon delete" data-delete-id="1">Delete</a>
                </div>
            </div>
    
       <!-- Guitar Object #2 -->
            <div id="cosponsorsTemplate_2">
                <div class="formColumn1">@Html.LabelFor(x=>x.Guitar2)</div>
                <div class="formColumn2">@Html.TextBoxFor(x => x.Guitar2.Make, new { Placeholder = "Make" })
                    <div class="messageBottom">@Html.ValidationMessageFor(x => x.Guitar2.Make)</div>
                </div>
                <div class="formColumn3">@Html.TextBoxFor(x => x.Guitar2.Model, new { Placeholder = "Model" })
                    <div class="messageBottom">@Html.ValidationMessageFor(x => x.Guitar2.Model)</div>
                </div>
                <div class="formColumn4">@Html.TextBoxFor(x => x.Guitar2.ProductionYear, new { Placeholder = "Production Year" })
                    <div class="messageBottom">@Html.ValidationMessageFor(x => x.Guitar2.ProductionYear)</div>
                    <a class="icon delete" data-delete-id="2">Delete</a>
                </div>
            </div>
    
        <!-- Guitar Object #3 -->
            <div id="cosponsorsTemplate_3">
                <div class="formColumn1">@Html.LabelFor(x=>x.Guitar3)</div>
                <div class="formColumn2">@Html.TextBoxFor(x => x.Guitar3.Make, new { Placeholder = "Make" })
                    <div class="messageBottom">@Html.ValidationMessageFor(x => x.Guitar3.Make)</div>
                </div>
                <div class="formColumn3">@Html.TextBoxFor(x => x.Guitar3.Model, new { Placeholder = "Model" })
                    <div class="messageBottom">@Html.ValidationMessageFor(x => x.Guitar3.Model)</div>
                </div>
                <div class="formColumn4">@Html.TextBoxFor(x => x.Guitar3.ProductionYear, new { Placeholder = "Production Year" })
                    <div class="messageBottom">@Html.ValidationMessageFor(x => x.Guitar3.ProductionYear)</div>
                    <a class="icon delete" data-delete-id="3">Delete</a>
                </div>
            </div>
    

    2 Answers 2

    2

    The documentation states that complex graphs should use a specialized validator. You can utilize it like so:

    public class Customer {
      public string Name { get; set; }
      public Address Address { get; set; }
    }
    
    public class Address {
     public string Line1 { get; set; }
     public string Line2 { get; set; }
     public string Town { get; set; }
     public string County { get; set; }
     public string Postcode { get; set; }
    }
    
    public class AddressValidator : AbstractValidator<Address> {
      public AddressValidator() {
        RuleFor(address => address.Postcode).NotNull();
        //etc
      }
    }
    
    public class CustomerValidator : AbstractValidator<Customer> {
      public CustomerValidator() {
        RuleFor(customer => customer.Name).NotNull();
        RuleFor(customer => customer.Address).SetValidator(new AddressValidator())
      }
    

    }

    From: Re-using Validators for Complex Properties

    http://fluentvalidation.codeplex.com/wikipage?title=CreatingAValidator&referringTitle=Documentation

    For your use case, you would create a Guitar Validator and a model validator for the aggregation object. I remember a friend running into similar problems because the validator doesn't know how to traverse the object graph without the intermediary validator.

    Sign up to request clarification or add additional context in comments.

    4 Comments

    Patrick, Thanks. I did try that, after you suggested it and same results.
    Mvc3 or Mvc4? Make sure you are using the correct version per MVC version. Also, can you show the output from the view and not the view contents? The validators will produce a standard markup, you can see the example between unobtrusive on vs unobtrusive off here: bradwilson.typepad.com/blog/2010/10/…. Make sure you turn on onobtrusive either in your web.config or via code.
    @PatrickHuber Can you explain me how you treat the Id object if Customer have ID property.
    @PatrickHuber Using Mvc3. I confirmed that the correct FluentValidation package is installed and also provided a snippet of the generated HTML, which is included in my updated question text.
    0

    I figured out the issue.

    The Guitar class

    public class Guitar
    {
        public string Model { get; set; }
        public int? ProductionYear { get; set; } 
        public string Make { get; set; }
    }
    

    Needs a Validator type decorator

     [Validator(typeof(GuitarValidator))]
    



    Correct

        [Validator(typeof(GuitarValidator))]
        public class Guitar
        {
            public string Model { get; set; }
            public int? ProductionYear { get; set; } 
            public string Make { get; set; }
        }
    

    1 Comment

    I prefer Patrick's approach. Using attributes means that the project containing your classes required a dependency on FluentValidation, whereas using SetValidator doesn't. The same principle applies for fluent configurations in EF.

    Your Answer

    By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

    Start asking to get answers

    Find the answer to your question by asking.

    Ask question

    Explore related questions

    See similar questions with these tags.