Last week we reviewed using Neat Architecture for Showpitch‘s Logging. This week we’re going to go in to Neat and Validation on Showpitch.
We’re all familiar with the need for Validation when building an application. Maybe there’s a property that should be an email, or maybe there’s an integer that should always be below a certain amount. In any given application there’s going to be a lot of these Validation concerns that come up. And just as with Logging we’re going to end up writing a lot of the same code, over and over again, unless there’s a better way.
Validation, Over and Over
Lets look at a typical Validation scenario:
public class Thing
{
public string ThingAMaJig { get; set; }
public int ThingAMaBob { get; set; }
}
public class AThingAddingClass : IAThingAddingClass
{
private readonly IThingRepository _thingRepository;
public AThingAddingClass(IThingRepository thingRespository)
{
_thingRepository = thingRepository;
}
public void AddANewThing(Thing aThing)
{
if(aThing.ThingAMaJig == null)
{
throw new ValidationException("ThingAMaJig Required!");
}
_thingRepository.Save(aThing);
}
}
That’s really simple, but now imagine writing this out each time you’re going to do anything with that Thing, and then extrapolate that out to hundreds of times in every application, for all of the properties you’re going to validate! Consider:
public class AThingAddingClass : IAThingAddingClass
{
private readonly IThingRepository _thingRepository;
public AThingAddingClass(IThingRepository thingRespository)
{
_thingRepository = thingRepository;
}
public void AddANewThing(Thing aThing)
{
if(aThing.ThingAMaJig == null)
{
throw new ValidationException("ThingAMaJig Required!");
}
_thingRepository.Save(aThing);
}
public void UpdateAThing(Thing aThing)
{
if(aThing.ThingAMaJig == null)
{
throw new ValidationException("ThingAMaJig Required!");
}
_thingRepository.Update(aThing);
}
public void IndexAThing(Thing aThing)
{
if(aThing.ThingAMaJig == null)
{
throw new ValidationException("ThingAMaJig Required!");
}
_thingRepository.Index(aThing);
}
public void DefaultAThing(Thing aThing)
{
if(aThing.ThingAMaJig == null)
{
throw new ValidationException("ThingAMaJig Required!");
}
aThing.ThingAMaBob = 7;
_thingRepository.Save(aThing);
}
}
Things are already starting to get out of hand!
Validation, A Validator For Every Object
So we can start to address this issue with a new object, specifically one that validates that Thing for us:
public class AThingValidatingClass : IAThingValidatingClass
{
public void ValidateAThing(Thing aThing)
{
if(aThing.ThingAMaJig == null)
{
throw new ValidationException("ThingAMaJig Required!");
}
}
}
Let’s try using this in the previous out of control example:
public class AThingAddingClass : IAThingAddingClass
{
private readonly IThingRepository _thingRepository;
private readonly IAThingValidatingClass _aThingValidatingClass;
public AThingAddingClass(IThingRepository thingRespository, IAThingValidatingClass aThingValidatingClass)
{
_thingRepository = thingRepository;
_aThingValidatingClass = aThingValidatingClass;
}
public void AddANewThing(Thing aThing)
{
_aThingValidatingClass.ValidateAThing(aThing);
_thingRepository.Save(aThing);
}
public void UpdateAThing(Thing aThing)
{
_aThingValidatingClass.ValidateAThing(aThing);
_thingRepository.Update(aThing);
}
public void IndexAThing(Thing aThing)
{
_aThingValidatingClass.ValidateAThing(aThing);
_thingRepository.Index(aThing);
}
public void DefaultAThing(Thing aThing)
{
_aThingValidatingClass.ValidateAThing(aThing);
aThing.ThingAMaBob = 7;
_thingRepository.Save(aThing);
}
}
Well that certainly shortens things up. But we still have a couple of issues here. First, we’d now have to develop a Validator for every object we want to Validate. Second, we seem to have more than one responsibility in our methods now. Before our methods just did something with a Thing, but now they validate a Thing and then do something with it.
Validation, A Validator and a Decorator For Every Object Then
So lets address the second issue first. We could separate the responsibilities, just like with Logging, using a Decorator:
public class AThingAddingClass : IAThingAddingClass
{
private readonly IThingRepository _thingRepository;
public AThingAddingClass(IThingRepository thingRespository)
{
_thingRepository = thingRepository;
}
public void AddANewThing(Thing aThing)
{
_thingRepository.Save(aThing);
}
public void UpdateAThing(Thing aThing)
{
_thingRepository.Update(aThing);
}
public void IndexAThing(Thing aThing)
{
_thingRepository.Index(aThing);
}
public void DefaultAThing(Thing aThing)
{
aThing.ThingAMaBob = 7;
_thingRepository.Save(aThing);
}
}
public class AThingAddingClassValidationDecorator : IAThingAddingClass
{
private readonly IAThingAddingClass _aThingAddingClass;
private readonly IAThingValidatingClass _aThingValidatingClass;
public AThingAddingClass(IAThingAddingClass aThingAddingClass, IAThingValidatingClass aThingValidatingClass)
{
_aThingAddingClass = aThingAddingClass;
_aThingValidatingClass = aThingValidatingClass;
}
public void AddANewThing(Thing aThing)
{
_aThingValidatingClass.ValidateAThing(aThing);
_aThingAddingClass.AddANewThing(aThing);
}
public void UpdateAThing(Thing aThing)
{
_aThingValidatingClass.ValidateAThing(aThing);
_aThingAddingClass.UpdateAThing(aThing);
}
public void IndexAThing(Thing aThing)
{
_aThingValidatingClass.ValidateAThing(aThing);
_aThingAddingClass.IndexAThing(aThing);
}
public void DefaultAThing(Thing aThing)
{
_aThingValidatingClass.ValidateAThing(aThing);
aThing.ThingAMaBob = 7;
_aThingAddingClass.DefaultAThing(aThing);
}
}
So now we only have one responsibility per method, but we still have the issue of having to build a Validator for each object. And on top of that, now we have to build a Decorator for each object as well!
Validation, A Better Way
Just as when we reviewed Logging, we know there’s a better way using interceptors. But how can we build something so generic for Validation?
Luckily for us, .Net has had a solution available for quite a while in the form of System.ComponentModel.DataAnnotations. These have been used in ASP.Net MVC for quite a while, but we can leverage them to build a Validation interceptor in to Neat, and we have!
Using System.ComponentModel.DataAnnotations we can declare how to Validate each property right on that property! So now our Thing becomes:
using System.ComponentModel.DataAnnotations;
public class Thing
{
[Required]
public string ThingAMaJig { get; set; }
public int ThingAMaBob { get; set; }
}
From here, our Validation interceptor can pick up any of a large number of System.ComponentModel.DataAnnotations and Validate the value in the property based on them. We’ve built the rest of that in to the ApplicationProcessing interceptor in Neat, by way of an ApplicationProcessingRule for Validation. You can take a look at the objects that leverage this for yourself in Neat:
- Neat.Infrastructure.Unity.Interceptor.ApplicationProcessingInterceptor
- Neat.Infrastructure.Unity.ApplicationProcessing.ApplicationProcessor
- Neat.Infrastructure.Unity.ApplicationProcessing.IApplicationProcessingRule
- Neat.Infrastructure.Validation.ApplicationProcessing.ValidationApplicationProcessingRule
- Neat.Model.NeatExample
This leaves us with a much simpler and more concise class than any of the other options, without having to build a Decorator ourselves:
public class AThingAddingClass : IAThingAddingClass
{
private readonly IThingRepository _thingRepository;
public AThingAddingClass(IThingRepository thingRespository)
{
_thingRepository = thingRepository;
}
public void AddANewThing(Thing aThing)
{
_thingRepository.Save(aThing);
}
public void UpdateAThing(Thing aThing)
{
_thingRepository.Update(aThing);
}
public void IndexAThing(Thing aThing)
{
_thingRepository.Index(aThing);
}
public void DefaultAThing(Thing aThing)
{
aThing.ThingAMaBob = 7;
_thingRepository.Save(aThing);
}
}
But how do you implement the System.ComponentModel.DataAnnotations for Validation? Lets look at an example:
public class ValidationResponse
{
public ValidationResponse()
{
ValidationErrors = new List();
IsValid = true;
}
public bool IsValid { get; set; }
public List ValidationErrors { get; set; }
}
// SNIP!
private ValidationResponse Validate(object objectToValidate)
{
var validationResponse = new ValidationResponse();
var results = new List();
if (!Validator.TryValidateObject(objectToValidate, new System.ComponentModel.DataAnnotations.ValidationContext(objectToValidate), results, true))
{
validationResponse.IsValid = false;
validationResponse.ValidationErrors.AddRange(results.Select(validationResult => validationResult.ErrorMessage));
}
return validationResponse;
}
// SNIP!
That’s it!
Validation can quickly become a lot of work in an application, but by using Neat, especially the Validation interceptor and System.ComponentModel.DataAnnotations Validation can be added in to an application quickly and easily. We’ve used this Validation at Showpitch to very quickly spin up new Domain Objects and make them available for use while protecting the data from invalid inputs and states. It has saved and will continue to save hours of work for Showpitch!
Would you like to know more about something I’ve written about, or do you want to see me cover something in the next post? Are there parts of Neat or Showpitch you’d like to learn more about? Please comment below! And check back for more!
You must be logged in to post a comment.