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.


Hoi Leon,
Ik vind dit wel een hele leuke en praktische toepassing van yield-return. Die moet ik onthouden.
Oh-ja, en de IValidatableObject is ook wel leuk ;-)
Gr, Marco
LOL got another one on yield coming… if I can find the time here in this German forrest ;-)
Do I really have to speak English on this blog? Oder zol ich im Deutsch scheiben, zowar stu in Deutsland an spazieren bist.
只要,因为它是 C#,我觉得一切都很好