Quantcast
Channel: Development With A Dot
Viewing all articles
Browse latest Browse all 404

Entity Framework Code First Fluent Validation

$
0
0

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:publicstaticclass DbContextExtensions
   2: {
   3:privatestatic IDictionary<Type, Tuple<Delegate, String>> entityValidations = new ConcurrentDictionary<Type, Tuple<Delegate, String>>();
   4:  
   5:publicstaticvoid AddEntityValidation<TEntity>(this DbContext context, Func<TEntity, Boolean> validation, String message) where TEntity : class
   6:     {
   7:if (context == null)
   8:         {
   9:thrownew ArgumentNullException("context");
  10:         }
  11:  
  12:if (validation == null)
  13:         {
  14:thrownew ArgumentNullException("validation");
  15:         }
  16:  
  17:if (String.IsNullOrWhiteSpace(message) == true)
  18:         {
  19:thrownew 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:privatestatic 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.


Viewing all articles
Browse latest Browse all 404

Trending Articles



<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>