2

I'm using Foolproof Validation so I can use [RequiredIf] attributes on my view model. The problem is that I'd like to trigger validation within my code using the same logic outside a controller.

I've tried creating my own validation context and using Validatior.TryValidateObject; however, it fails with Foolproof's custom RequiredIf validator. Is there a way to take my model and validate it other than passing it to a controller?

Am I using the wrong approach?

Here is my code:

var draftModel = _draftHelper.LoadDraft(draftId);

var validationResults = new List<ValidationResult>();

var vc = new ValidationContext(draftModel, null, null);

var isValidDraft = Validator.TryValidateObject(draftModel, vc, 
                                               validationResults, true);

And the error I get is on the TryValidateObject line

System.NotImplementedException: The method or operation is not implemented.

Michael La Voie
  • 27,772
  • 14
  • 72
  • 92

2 Answers2

4

I think a better approach would be to use FluentValidation, not foolproof validation. I personally think Fluent is nicer than attributes too :).

Using FluentValidation, you are also able to validate your model without the use of a Controller.

DraftVM draft = draftRepository.Get(draftId);

var DraftValidator validator = new DraftVMValidator();
ValidationResult results = validator.Validate(draft);

public class DraftVMValidator : AbstractValidator<DraftViewModel>
{
      public DraftVMValidator()
      {

         RuleFor(vm => vm.OptionalField)
            .Must(BeFilledIfNameNotEmpty)
            .WithMessage("Optional Field required because you filled out Name field");
      }


      public bool BeFilledIfNameNotEmpty(DraftVM viewModel)
      {
            return !string.IsNullOrWhiteSpace(viewModel.Name);
      }

} 

This will NOT give you a System.NotImplemented exception.

This validator is DRY because you can also plug it into ASP.NET MVC Validation.
You can simply call the following code in Global.asax or App_Start etc. One validator for all, bind it to MVC Model Validation or use it in any normal application.

FluentValidationModelValidatorProvider.Configure(); // This will bind it for you

If you use Inversion of Control container like Ninject, FluentValidation also has a plugin to work with that. More available on their documentation in link provided above.

I have a pretty big project example in my Github if you want to see more examples of this Validator instead of FoolProof. Example Validators with ASP.NET MVC 4

Patrick Magee
  • 2,951
  • 3
  • 33
  • 50
  • Thanks @Patrick Magee, that may be the right approach. I hadn't realized that Fluent also supported client side validation. I'll test it today and if it also provides custom client side validation then this will be my answer. – Michael La Voie May 13 '13 at 16:52
  • You can also get away with FluentValidation using REMOTE validation for things like Emails which need to be unique using Ajax to validate without the user having to post back the form. There's an example of that in my project too. Since the Predicate (Must) function doesnt support remote validation by default. You could create custom binders though. – Patrick Magee May 13 '13 at 17:01
  • Thanks @Patrick Magee, I tested your solution and it works great. This solved my problem. As for [Remote] that would work; however, for I still prefer client side custom validators, if possible. – Michael La Voie May 14 '13 at 23:03
2

You need to let the MVC framework provide your Validator instead of using the Validator.TryValidateObject as below:

var modelMetadata = ModelMetadataProviders.Current.GetMetadataForType(() => viewModelToValidate, viewModelToValidate.GetType());
var compositeValidator = ModelValidator.GetModelValidator(modelMetadata, controller.ControllerContext);
foreach (ModelValidationResult result in compositeValidator.Validate(null))
{
   validationResults.Add(new ValidationResult(result.Message, new List<string> { result.MemberName }));
}
Alexis Pigeon
  • 7,423
  • 11
  • 39
  • 44