Entity Framework - Entity Validations


Entity Framework provides validation features of different varities that can be used through a user interface for client-side validation or be used for server-side validation. When using code first approach, you can specify validations using annotation or fluent API configurations. Additional validations, and more complex, can be specified in code and will work whether your model hails from code first, model first or database first.

Validations with a simple pair of classes: Book and Publisher

public class Book
      {
          public int BookId { get; set; }
          public string BookName { get; set; }
          public DateTime DateCreated { get; set; }
          public virtual ICollection Publishers { get; set; }
      }

      public class Publisher
      {
          public int PublisherId { get; set; }
          public string PublisherName { get; set; }
          public DateTime DateCreated { get; set; }
          public int BookId { get; set; }
      }

Data Annotations
Code First uses annotations from the System.ComponentModel.DataAnnotations assembly. These annotations provides rules such as the Required, MaxLength and MinLength. A number of .NET client applications also recognize these annotations, for example, ASP.NET MVC. Both client side and server side validation can be achieved with these annotations. 

For example, force the Book Name property to be a required property

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

Fluent API
Use code first’s fluent API instead of data annotations to get the same client side & server side validation. 

Fluent API configurations are applied as code first is building the model from the classes. You can inject the configurations by overriding the DbContext class’ OnModelCreating method. 

For example : Here is a configuration specifying that the BookName property can't be longer than 100 characters

public class BookContext : DbContext
      {
          public DbSet Books { get; set; }
          public DbSet Publishers { get; set; }

          protected override void OnModelCreating(DbModelBuilder modelBuilder)
          {
              modelBuilder.Entity().Property(p => p.BookName).HasMaxLength(100);
          }
       }

The validation doesn’t automatically get passed back into the view which is why the additional code that uses ModelState.AddModelError is being used. This ensures that the error details make it to the view which will then use the ValidationMessageFor Htmlhelper to display the error. Exception handling error code in the application’s BookController class that captures that validation error when Entity Framework attempts to save a book with a BookName that exceeds the 100 character maximum.

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

[HttpPost]
    public ActionResult Edit(int BookId, Book book)
    {
        try
        {
            db.Entry(book).State = EntityState.Modified;
            db.SaveChanges();
            return RedirectToAction("Index");
        }
        catch(DbEntityValidationException ex)
        {
            var error = ex.EntityValidationErrors.First().ValidationErrors.First();
            this.ModelState.AddModelError(error.PropertyName, error.ErrorMessage);
            return View();
        }
    }

IValidatableObject
IValidatableObject is an interface in System.ComponentModel.DataAnnotations. Although it is not part of the Entity Framework API, you can still use it for server-side validation in Entity Framework classes. IValidatableObject provides a Validate method that Entity Framework call during SaveChanges or can call any time you want to validate the classes. For example the Book class has been extended to implement IValidatableObject and then provide a rule that the BookName and AuthorName cannot match.

public class Book : IValidatableObject
     {
          public int BookId { get; set; }
          [Required]
          public string BookName { get; set; }
          public string AuthorName { get; set; }
          public DateTime DateCreated { get; set; }
          public virtual ICollection Publishers { get; set; }

         public IEnumerable Validate(ValidationContext validationContext)
         {
             if (AuthorName == BookName)
             {
                 yield return new ValidationResult
                  ("Book Name cannot match Author Name", new[] { "BookName", “AuthorName” });
             }
         }
     }

Important points when using validation
Lazy loading is disabled during validation.
EF validates data annotations on non-mapped properties (properties that are not mapped to a column in the database).
Validation is performed after changes are detected during SaveChanges.
DbUnexpectedValidationException is thrown if errors occur during validation.
Facets that Entity Framework includes in the model (maximum length, required, etc.) will cause validation, even if there are not data annotations on your classes and/or you used the EF Designer to create your model.

Precedence Rules
Fluent API calls override the corresponding data annotations

Execution Order
Property validation occurs before type validation
Type validation only occurs if property validation succeeds
If a property is complex its validation will also include:
Property-level validation on the complex type properties
Type level validation on the complex type, including IValidatableObject validation on the complex type

Praesent mattis

Pellentesque viverra vulputate enim. Aliquam erat volutpat. Pellentesque tristique ante ut risus. Quisque dictum. Integer nisl risus, sagittis convallis, rutrum id, elementum congue, nibh. Suspendisse dictum porta lectus. Donec placerat odio vel elit.

Read More