Development With A Dot

Blog on development in general, and specifically on .NET

Sponsors

News

My Friends

My Links

Permanent Posts

Portuguese Communities

Entity Framework Code First Fluent Validation

Back to Entity Framework Code First (EFCF) validation. On my previous post I mentioned that EFCF did not support fluent validation. While this is true, it isn’t too hard to implement one such mechanism, which is exactly why I am writing this! Smile

I will be using the SavingChanges event to inject the validation logic, which will be implemented by strongly typed delegates. Let’s see some code:

   1: public static class DbContextExtensions
   2: {
   3:     private static IDictionary<Type, Tuple<Delegate, String>> entityValidations = new ConcurrentDictionary<Type, Tuple<Delegate, String>>();
   4:  
   5:     public static void AddEntityValidation<TEntity>(this DbContext context, Func<TEntity, Boolean> validation, String message) where TEntity : class
   6:     {
   7:         if (context == null)
   8:         {
   9:             throw new ArgumentNullException("context");
  10:         }
  11:  
  12:         if (validation == null)
  13:         {
  14:             throw new ArgumentNullException("validation");
  15:         }
  16:  
  17:         if (String.IsNullOrWhiteSpace(message) == true)
  18:         {
  19:             throw new ArgumentNullException("message");
  20:         }
  21:  
  22:         if (entityValidations.ContainsKey(typeof(TEntity)) == false)
  23:         {
  24:             (context as IObjectContextAdapter).ObjectContext.SavingChanges += delegate
  25:             {
  26:                 if (context.Configuration.ValidateOnSaveEnabled == true)
  27:                 {
  28:                     IEnumerable<TEntity> entities = context.ChangeTracker.Entries<TEntity>().Where(x => x.State == EntityState.Added || x.State == EntityState.Modified).Select(x => x.Entity).ToList();
  29:  
  30:                     foreach (TEntity entity in entities)
  31:                     {
  32:                         String error = ValidateEntity(entity);
  33:  
  34:                         if (String.IsNullOrWhiteSpace(error) == false)
  35:                         {
  36:                             throw (new ValidationException(error));
  37:                         }
  38:                     }
  39:                 }
  40:             };
  41:         }
  42:  
  43:         entityValidations[typeof(TEntity)] = new Tuple<Delegate, String>(validation, message);
  44:     }
  45:  
  46:     private static String ValidateEntity<TEntity>(TEntity entity)
  47:     {
  48:         Type entityType = typeof(TEntity);
  49:  
  50:         if (entityValidations.ContainsKey(entityType) == true)
  51:         {
  52:             Tuple<Delegate, String> entry = entityValidations[entityType];
  53:             Func<TEntity, Boolean> validation = entry.Item1 as Func<TEntity, Boolean>;
  54:  
  55:             if (validation(entity) == false)
  56:             {
  57:                 return (entry.Item2);
  58:             }
  59:         }
  60:  
  61:         return (null);
  62:     }
  63: }

We have an extension method that allows declaring, for an entity type, a validation expression, such as this:

   1: ctx.AddEntityValidation<SomeEntity>(x => x.SomeProperty != null, "SomeProperty is required");

The validation will be fired when the SaveChanges method is called and the errors will be encapsulated in a ValidationException:

   1: try
   2: {
   3:     ctx.SaveChanges();
   4: }
   5: catch (ValidationException ex)
   6: {
   7:     //see content of ex.ValidationResult.ErrorMessage
   8: }

This code can certainly be improved – multiple validations per entity, property-based validations, etc – but I think it is good enough to illustrate my technique.

One final note: the fluent validation will only be fired if the ValidateOnSaveEnabled property is set to true, which is the default.

Comments

No Comments