Capturing Code First Fluent API ValidationResults to Display in MVC 3

If you’ve been using code first, you may be aware  that you can add validations such as Required or MaxLength to your classes using DataAnnotations, e.g.,

[Required]
public string Title{get ;set;}

or using the fluent API:

modelBuilder.Entity<Blog>().Property(p => p.Title).IsRequired();

MVC 3 also recognizes Data Annotations, and the validation defined in the [Required] annotation will flow through to the UI seamlessly.

BLOG POST titlerequired

 

But MVC 3 doesn’t recognize the the fluent api validation. The controller is aware of them but MVC isn’t able to automatically access them for some reason that I just cannot figure out.

Here for example is an Edit method in a controller class:

[HttpPost]
public ActionResult Edit(int id, Blog blog)
{
    try
    {
        db.Entry(blog).State = EntityState.Modified;
        db.SaveChanges();
        return RedirectToAction("Index");
    }
    catch
{
return View(); } }

An alternate patter is to use ModelState.IsValid. But the validation problem discovered by the fluent api is not caught here!

With a method like this:

  [HttpPost]
        public ActionResult Edit(Blog blog)
        {
            if (ModelState.IsValid)
            {
                db.Entry(blog).State = EntityState.Modified;
                db.SaveChanges();
                return RedirectToAction("Index");
            }
            return View(blog);
        }

You’ll get an ugly error

BLOG POST error

because you have no exception handler.

If you were to capture the exception and inspect the ValidationResult, it looks exactly the same as the ValidationResult returned by the DataAnnotation validation failure. In the following code, it is the error variable that I’m inspecting to check the result.

    catch(Exception ex)
    {
        if (ex is System.Data.Entity.Validation.DbEntityValidationException)
        {
            var ve = ex as DbEntityValidationException;
            var error = ve.EntityValidationErrors.First().ValidationErrors.First();
        }
        return View();
    }   

I spent quite a lot of time looking at the error and couldn’t figure out why MVC wasn’t displaying the message. Finally I started debugging the view itself and poking around saw that the model contained error information in the first case (data annotation) but not in the second (fluent api).

So I returned to the exception and manually stuffed the error I discovered into the View’s model.

    catch(Exception ex)
    {
        if (ex is System.Data.Entity.Validation.DbEntityValidationException)
        {
            var ve = ex as DbEntityValidationException;
            var error = ve.EntityValidationErrors.First().ValidationErrors.First();
            this.ModelState.AddModelError(error.PropertyName, error.ErrorMessage);         }
        return View();
    }

With that, my error message was displayed.

Finally, I tweaked the handler to capture all possible errors for that entity, not just the first one:

catch (DbEntityValidationException ex)
{
        var errors = ex.EntityValidationErrors.First(); //.ValidationErrors.First();
        foreach (var propertyError in errors.ValidationErrors)
        {
            this.ModelState.AddModelError
(propertyError.PropertyName, propertyError.ErrorMessage); } return View(); }

Here now is the view that is capturing not just title is required but a maxlength attribute I added to the blogger’s name property:

BLOG POST maxlength

In my testing, I found that if I use ModelState.IsValid and the exception handler I wouldn’t catch the fluent api excption if there was also a required validation problem (not sure why you would use both, but I checked anyway). But if I skip the IsValid, both problems will be detected in the exception handler.

EF 4.1 also lets you validate using the ValidateEntity method override of DbContext. MVC won’t automatically recognize these either but the exception handler will be sure the model has the validation error information.

8 thoughts on “Capturing Code First Fluent API ValidationResults to Display in MVC 3

  1. Wondering if it might be just a bit cleaner if you caught DbEntityValidationException directly, instead of catching Exception, then checking the type, then casting it into a new variable…

  2. yes, this was a residue of me trying to see what was going on. SO now that I know it’s a DbEntityValidation exception I can catch that directly.

  3. When you add the [Required] attribute you are using a DataAnnotation to tell everything it is required. EF Code First can read various DataAnnotations and take them into consideration when building your model.

    When you use the fluent API you are configuring your model directly – not adding validation.

    MVC does not read EF’s model in the absence of data annotations. You would get exactly the same behavior if you used POCO classes + EDMX via model/database first.

    [)amien

  4. thanks for the clarification on why it works that way, Damien. I struggled with this for a while and nobody seemed to be able to help me so I had to keep at it and find my own way.

Leave a Reply

Your email address will not be published. Required fields are marked *