BrunoLM
BrunoLM

Reputation: 100381

How to manually validate a model with attributes?

I have a class called User and a property Name

public class User
{
    [Required]
    public string Name { get; set; }
}

And I want to validate it, and if there are any errors add to the controller's ModelState or instantiate another modelstate...

[HttpPost]
public ActionResult NewUser(UserViewModel userVM)
{
    User u = new User();
    u.Name = null;

    /* something */

    // assume userVM is valid
    // I want the following to be false because `user.Name` is null
    if (ModelState.IsValid)
    {
        TempData["NewUserCreated"] = "New user created sucessfully";

        return RedirectToAction("Index");
    }

    return View();
}

The attributes works for UserViewModel, but I want to know how to validate a class without posting it to an action.

How can I accomplish that?

Upvotes: 98

Views: 86837

Answers (5)

Electrionics
Electrionics

Reputation: 6782

There is another approach to validation, which is more easy reusable - FluentValidation

This library allows to play with inheritance, different rule sets for one model and has many other cool features. Read about advantages here.

With this library definition of validation rules is separated from model and code will look next way:

public class UserValidator:AbstractValidator<User>
{
    public UserValidator()
    {
        RuleFor(x => x.Name).NotEmpty();
    }
}

Usage will look next way:

var validator = new UserValidator();
var validationResult = await validator.ValidateAsync(model);    

Upvotes: 1

James Santiago
James Santiago

Reputation: 3072

You can use Validator to accomplish this.

var context = new ValidationContext(u, serviceProvider: null, items: null);
var validationResults = new List<ValidationResult>();

bool isValid = Validator.TryValidateObject(u, context, validationResults, true);

Upvotes: 144

Brian MacKay
Brian MacKay

Reputation: 32037

I wrote a wrapper to make this a bit less clunky to work with.

Usage:

var response = SimpleValidator.Validate(model);

var isValid = response.IsValid;
var messages = response.Results; 

Or if you only care about checking validity, it's even tighter:

var isValid = SimpleValidator.IsModelValid(model);

Complete source:

using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;

namespace Ether.Validation
{
    public static class SimpleValidator
    {
        /// <summary>
        /// Validate the model and return a response, which includes any validation messages and an IsValid bit.
        /// </summary>
        public static ValidationResponse Validate(object model)
        {
            var results = new List<ValidationResult>();
            var context = new ValidationContext(model);

            var isValid = Validator.TryValidateObject(model, context, results, true);
         
            return new ValidationResponse()
            {
                IsValid = isValid,
                Results = results
            };
        }

        /// <summary>
        /// Validate the model and return a bit indicating whether the model is valid or not.
        /// </summary>
        public static bool IsModelValid(object model)
        {
            var response = Validate(model);

            return response.IsValid;
        }
    }

    public class ValidationResponse
    {
        public List<ValidationResult> Results { get; set; }
        public bool IsValid { get; set; }

        public ValidationResponse()
        {
            Results = new List<ValidationResult>();
            IsValid = false;
        }
    }
}

Or at this gist: https://gist.github.com/kinetiq/faed1e3b2da4cca922896d1f7cdcc79b

Upvotes: 12

ˈvɔlə
ˈvɔlə

Reputation: 10272

Since the question is asking specifically about ASP.NET MVC, you can use the TryValidateObject inside your Controller action.

Your desired method overload is TryValidateModel(Object)

Validates the specified model instance.

Returns true if the model validation is successful; otherwise false.

Your modified source code

[HttpPost]
public ActionResult NewUser(UserViewModel userVM)
{
    User u = new User();
    u.Name = null;

    if (this.TryValidateObject(u))
    {
        TempData["NewUserCreated"] = "New user created sucessfully";
        return RedirectToAction("Index");
    }

    return View();
}

Upvotes: 11

Maxime
Maxime

Reputation: 2260

I made an entry in the Stack Overflow Documentation explaining how to do this:

Validation Context

Any validation needs a context to give some information about what is being validated. This can include various information such as the object to be validated, some properties, the name to display in the error message, etc.

ValidationContext vc = new ValidationContext(objectToValidate); // The simplest form of validation context. It contains only a reference to the object being validated.

Once the context is created, there are multiple ways of doing validation.

Validate an Object and All of its Properties

ICollection<ValidationResult> results = new List<ValidationResult>(); // Will contain the results of the validation
bool isValid = Validator.TryValidateObject(objectToValidate, vc, results, true); // Validates the object and its properties using the previously created context.
// The variable isValid will be true if everything is valid
// The results variable contains the results of the validation

Validate a Property of an Object

ICollection<ValidationResult> results = new List<ValidationResult>(); // Will contain the results of the validation
bool isValid = Validator.TryValidatePropery(objectToValidate.PropertyToValidate, vc, results, true); // Validates the property using the previously created context.
// The variable isValid will be true if everything is valid
// The results variable contains the results of the validation

And More

To learn more about manual validation see:

Upvotes: 71

Related Questions