Entity Framework Validation with IValidatableObject Example

With Entity Framework, data annotations are a great way to validate entity data from a single property. But what if we want to check entity data against two or more properties? For more complex validation strategies we can implement IValidatableObject in our poco classes.

In this example we have a simple User poco with a Name and Nick property. We need to ensure that Name and Nick are not equal and Nick starts with an @ sign. To do this we implement the Data Annotations IValidatableObject interface which requires a Validate method. Validate returns a collection of ValidationResult.

using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
 
namespace CodeFirstMail.Pocos
{
    public class User : IEntity, IValidatableObject
    {
        public string Name { get; set; }
        public string Nick { get; set; }
        public string Address { get; set; }
        public int Id { get; set; }
 
        #region IValidatableObject Members
 
        public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
        {
            if (Name == Nick)
            {
                yield return new ValidationResult(
                    "Name and Nick must be different.",
                    new[] {"Name", "Nick"});
            }
 
            if (!Nick.StartsWith("@"))
            {
                yield return new ValidationResult(
                    "Nick must start with @.",
                    new[] {"Nick"});
            }
        }
 
        #endregion
    }
}

Let’s use the test from our former example on data annotations to check for these validation errors explicitly. Once we make a call to our DbContext SaveChanges method, the Validate method of each added or modified entity will be called automatically.

using System.Data.Entity;
using System.Data.Entity.Validation;
using System.Diagnostics;
using CodeFirstMail.Context;
using CodeFirstMail.Pocos;
using Microsoft.VisualStudio.TestTools.UnitTesting;
 
namespace CodeFirstTests
{
    [TestClass]
    public class MailContextTest
    {
        [TestMethod]
        [ExpectedException(typeof (DbEntityValidationException))]
        public void EntityValidationShouldThrowDbEntityValidationException()
        {
            Database.SetInitializer(
                new DropCreateDatabaseIfModelChanges<MailContext>());
 
            using (var mailContext = new MailContext())
            {
                try
                {
                    var user =
                        new User
                            {
                                Name = "Leon",  // The Name is fine, 
                                Nick = "Leon",  // but Nick is the same.
                            };
 
                    mailContext.Users.Add(user);
                    mailContext.SaveChanges();
                }
                catch (DbEntityValidationException ex)
                {
                    LogValidationErrors(ex);
                    throw;
                }
            }
        }
 
        private static void LogValidationErrors(DbEntityValidationException ex)
        {
            foreach (DbEntityValidationResult validation 
                in ex.EntityValidationErrors)
            {
                Debug.WriteLine(
                    "Error in entity {0}",
                    validation.Entry.Entity);
 
                foreach (DbValidationError propertyError 
                    in validation.ValidationErrors)
                {
                    Debug.WriteLine(
                        "Error in property {0}: {1}",
                        propertyError.PropertyName,
                        propertyError.ErrorMessage);
                }
            }
        }
    }
}

And here are the test results… Note the validation errors for Name as well as Nick for uniqueness.

Entity Framework IValidatableObject Test

4 thoughts on “Entity Framework Validation with IValidatableObject Example

Leave a Reply

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

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>