static void

Entity Framework Code First Validation

You can turn off validation completely (for instance, if an entity has already been validated).

//default to validate on save
context.Configuration.ValidateOnSaveEnabled = false;

Validation Rules

Use DataAnnotations:

[Required, StringLength(50)]
public string LastName { get; set; }

Or fluent mapping:

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    modelBuilder
        .Entity<Person>()
        .Property(p => p.LastName).HasMaxLength(50).IsRequired();

Or IValidatableObject (or IDataErrorInfo).

public class Customer : IValidatableObject
{
    public int Id { get; set; }
    public string FirstName { get; set; }
 
    public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
    {
        var results = new List<ValidationResult>();
        if (FirstName.Contains("\n"))
        {
            results.Add(
                new ValidationResult("Newline character is illegal",
                    new[] { "FirstName" }));
        }
        return results;
    }
}

Valildate In DbContext

The context has two extension points into validation. ValidateEntity allows custom validation to be added:

protected override DbEntityValidationResult ValidateEntity(
    DbEntityEntry entityEntry,
    IDictionary<object, object> items)
{
    //do the base DataAnnotations/fluent validation mapping
    var result = base.ValidateEntity(entityEntry, items);
 
    //do additional validation for customer
    var customer = entityEntry.Entity as Customer;
 
    //could be added or updated (change ShouldValidateEntity to include deleted)
    if (entityEntry.State == EntityState.Added &&
        customer != null)
    {
        if (customer.FirstName.Contains("\t"))
        {
            result.ValidationErrors.Add(
                new DbValidationError("LastName", "Illegal character"));
        }
    }
    return result;
}

ShouldValidateEntity is a simple boolean to add/exclude entities for validation.

protected override bool ShouldValidateEntity(
    DbEntityEntry entityEntry)
{
    if (entityEntry.Entity is Employee)
        return false; //don't validate employees
    if (entityEntry.Entity is Customer && entityEntry.State == EntityState.Deleted)
        return true; //validate deleted customers
    //default: validate all added and updated entities
    return base.ShouldValidateEntity(entityEntry);
}

Validate OnSave vs On Demand

You can turn on and off validation on save.

//default to validate on save
context.Configuration.ValidateOnSaveEnabled = false;

For OnSave (default) you'll get a DbEntityValidationException:

using (var context = new DomainContext())
{
    try
    {
        var newEmployee = new Employee { LastName = "Smith" };
        context.Employees.Add(newEmployee);
        context.SaveChanges();
    }
    //need a reference to System.Data (System.Data.Entity.Validation)
    catch (DbEntityValidationException entityValidationException)
    {
        foreach (var result in entityValidationException.EntityValidationErrors)
        {
            //the entity
            Console.WriteLine(result.Entry.Entity.ToString());
            foreach (var error in result.ValidationErrors)
            {
                Console.WriteLine(error.PropertyName + " " + error.ErrorMessage);
            }
        }
        throw;
    }
}

For OnDemand, you can do for all tracked entities (context), a specific entity or a specific property.

//validate all context
var allResults = context.GetValidationErrors();
//validate entity
var result = context.Entry(newEmployee).GetValidationResult();
//validate property
var errors = context.Entry(newEmployee).Property(x => x.LastName)
    .GetValidationErrors();