Announcing Simple.Validation

My company has graciously allowed me to develop a lightweight validation framework and publish it as open-source. There are other very nice validation frameworks available, so why another one? I wanted to be able to use something with all of the strengths and none of the weaknesses of the existing libraries. Here are my stated goals for the project:

  • Simplicity
  • Get moving with a minimum number of moving parts
  • Support dependency injection
  • Focus on object-level validation
  • Support for common property-level validation
  • Support all application types in a consistent manner
  • Support validation at all application layers in a consistent manner
  • Pluggable Architecture

The api I wanted for usage was very simple. Here’s some code from the LoanApplicationSample project:

    public class LoanApplicationDb
    {
        public void Save(LoanApplication loanApplication)
        {
            Validator.Enforce(loanApplication, "Save");
            // do save logic
        }

        public void Submit(LoanApplication loanApplication)
        {
            Validator.Enforce(loanApplication, "Save", "Submit");
            // do submit logic
        }
    }

Just pass your object to the Valdiator.Enforce() method along with the rules sets you wish to validate against and you are off to the races. Enforce will call Validator.Validate(), check for errors, and throw a ValidationException if it finds any. If it doesn’t find any, it just returns the results of Validate(). If you want to bypass the exception logic, just call Validate() directory.

The above code sample demonstrates how to consume the Validator API, but how do you plug into it? You need to be aware of two components. The first is the IValidator<T> implementation. Here is a sample validator that validates the LoanApplciation against the “Save” rules set.

    public class SaveLoanApplicationValidator : IValidator<LoanApplication>
    {
        public bool AppliesTo(string rulesSet)
        {
            return rulesSet == "Save";
        }

        public IEnumerable<ValidationResult> Validate(LoanApplication value)
        {
            var nameResults = Properties<LoanApplication>
                .For(e => e.Name)
                .Required()
                .Length(2, 100)
                .Message("Name is required.")
                .IgnoreWhiteSpace()
                .Validate(value)
                ;

            var reasonResults = Properties<LoanApplication>
                .For(e => e.Reason)
                .Required()
                .Length(10, 500)
                .IgnoreWhiteSpace()
                .Message("Reason is required.")
                .Validate(value)
                ;


            var accumulatedResults = nameResults
                .Concat(reasonResults)
            ;

            return accumulatedResults;
        }
    }

 

This validator uses the built-in PropertyValidator fluent syntax. Use of the built-in validators is entirely optional. They are included as a convenience in order to provide support for common property-level validations. Simple.Validation handles the simple stuff for you Smile. Where many validation frameworks fall down is when there is some kind of complex validation. In Simple.Validation, complex scenarios are handled the same way as property-level validation. Here’s an example:

        public IEnumerable<ValidationResult> Validate(LoanApplication value)
        {
            if (someComplexCondition)
                yield return new ValidationResult()
                                 {
                                     Context = value,
                                     Message = string.Format(messageFormat, arguments),
                                     PropertyName = "PropertyName",
                                     Severity = ValidationResultSeverity.Error,
                                     Type = MyCustomValidationType.SomeComplexConditionError
                                 };

The ValidationResult is a fairly rich object containing a lot of information about the validation error. The Context is the object being validated. You can (and should) provide a detailed message about the error. PropertyName is not required, but will be useful in many scenarios. Severity has levels of Error, Warning, and Informational. ValidationResult.Type is a property there for arbitrary categorization of validation results.

Okay, so now I’ve written a validator—but how does the static Validator class know about the validator I just wrote?

Validator uses an instance of IValidatorProvider to get the list of validators from the simple. Simple.Validation ships with a DefaultValidatorProvider which you can use as follows:

    public class Configuration
    {
        public void ConfigureValidation()
        {
            var validatorProvider = CreateValidatorProvider();
            RegisterValidators(validatorProvider);
            Validator.SetValidatorProvider(validatorProvider);
        }

        private static void RegisterValidators(DefaultValidatorProvider validatorProvider)
        {
            validatorProvider.RegisterValidator(new SaveLoanApplicationValidator());
            validatorProvider.RegisterValidator(new SubmitLoanApplicationValidator());
        }

        private static DefaultValidatorProvider CreateValidatorProvider()
        {
            var validatorProvider = new DefaultValidatorProvider();
            return validatorProvider;
        }
    }

The implementation of DefaultValidatorProvider is naïve, so you are encouraged to use the IValidatorProvider interface as a wrapper around your favorite Dependency Injection framework. We provide a second nuget package called Simple.Validation.Ninject that contains a NinjectValidatorProvider.

We’ve been using Simple.Valdiation internally for awhile and feel pretty good about its release. However, as it hasn’t been published on the public NuGet feed yet, we’re assigning a prelease version of 0.1. We will conform to semantic versioning conventions as outlined in this post from Phil Haack.

So, run Install-Package Simple.Validation from the Visual Studio Package Manager Console and start using the bits! As always, your feedback is much appreciated.

Happy Coding! Open-mouthed smile

Leave a Reply