Alex Dn
Alex Dn

Reputation: 5553

.NET MVC Custom validation (without data annotation)

use .NET MVC and code-first EF to implement of requested functionality. Business objects are relatively complex and I use System.ComponentModel.DataAnnotations.IValidatableObject to validate business object.
Now I'm trying to find the way, how to show validation result from business object, using MVC ValidationSummary without using data annotations. For example (very simplified):

Business Object:

    public class MyBusinessObject : BaseEntity, IValidatableObject
    {
        public virtual IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
        {
           return Validate();
        }
        public IEnumerable<ValidationResult> Validate()
        {
           List<ValidationResult> results = new List<ValidationResult>();

           if (DealType == DealTypes.NotSet)
           {
                results.Add(new ValidationResult("BO.DealType.NotSet", new[] { "DealType" }));
           }

           return results.Count > 0 ? results.AsEnumerable() : null;
        }
    }

Now in my MVC controller I have something like this:

    public class MyController : Controller
    {
        [HttpPost]
        public ActionResult New(MyModel myModel)
        {
           MyBusinessObject bo = GetBoFromModel(myModel);
           IEnumerable<ValidationResult> result = bo.Validate();
           if(result == null)
           {
               //Save bo, using my services layer
               //return RedirectResult to success page
           }

           return View(myModel);
        }
    }

In view, I have Html.ValidationSummary();.
How I can pass IEnumerable<ValidationResult> to the ValidationSummary?

I tried to find an answer by googling, but all examples I found describes how to show validation summary using data annotations in Model and not in Business object.

Thanks

Upvotes: 5

Views: 6877

Answers (4)

Tomas Jansson
Tomas Jansson

Reputation: 23462

I would have a look at FluentValidation. It's a framework for validation without data annoations. I've used it with great success in some projects for complex validation, and it is also usable outside of the MVC-project.

Here is the sample code from their page:

using FluentValidation;

public class CustomerValidator: AbstractValidator<Customer> {
  public CustomerValidator() {
    RuleFor(customer => customer.Surname).NotEmpty();
    RuleFor(customer => customer.Forename).NotEmpty().WithMessage("Please specify a first name");
    RuleFor(customer => customer.Company).NotNull();
    RuleFor(customer => customer.Discount).NotEqual(0).When(customer => customer.HasDiscount);
    RuleFor(customer => customer.Address).Length(20, 250);
    RuleFor(customer => customer.Postcode).Must(BeAValidPostcode).WithMessage("Please specify a valid postcode");
  }

  private bool BeAValidPostcode(string postcode) {
    // custom postcode validating logic goes here
  }
}

Customer customer = new Customer();
CustomerValidator validator = new CustomerValidator();
ValidationResult results = validator.Validate(customer);

bool validationSucceeded = results.IsValid;
IList<ValidationFailure> failures = results.Errors;

Upvotes: 4

Britton
Britton

Reputation: 2981

Entity Framework should throw a DbEntityValidationException if there are validation errors. You can then use the exception to add the errors to the ModelState.

try
{
     SaveChanges();
}
catch (DbEntityValidationException ex)
{
     AddDbErrorsToModelState(ex);
}
return View(myModel);

protected void AddDbErrorsToModelState(DbEntityValidationException ex)
{
     foreach (var validationErrors in ex.EntityValidationErrors)
     {
          foreach (var validationError in validationErrors.ValidationErrors)
          {
               ModelState.AddModelError(validationError.PropertyName, validationError.ErrorMessage);
          }
     }
}

Upvotes: 1

Dan Hunex
Dan Hunex

Reputation: 5318

Add property, say BusinessError, in the model

in the View do the following

  @Html.ValidationMessageFor(model => model.BusinessError)

Then in your controller whenever you have error do the following

ModelState.AddModelError("BussinessError", your error)

Upvotes: 12

ararog
ararog

Reputation: 1092

One of the ways to pass the contents of IEnumerate and keep taking advantage of Html.ValidationSummary is by updating ModelState.

You can find a good discussion on how to update the ModelState here.

Upvotes: 0

Related Questions