Validations

Fluent-ish Validations – Part 2 [Solution]

Part 1 is here

I started looking at the Validator source code to see how it works internally. If you didn’t know ASP.NET MVC and Entity Framework uses the Validator to perform their validations. The Validator uses the TypeDescriptor that’s good news as we can add attributes to objects and types at runtime with the TypeDescriptor.

Side Note: I was getting excited thinking about being able to add validation to specific object and not their type. For conditional validations but the Validator will always look at the object type to get the attributes and not the object itself.

Even though this is mainly about Validation in the end we are just adding attributes so I decided to not just called it Validation and name most things with the prefix of Attributes. Really once we are done we could add any attribute to a property. To start we are going to need to create a CustomTypeDescriptor.

To give a shout out I got the gist from this stackoverflow answer but I implemented it a bit different to make it more generic to what I want to do.

To make it simple we will take in the constructor an Enumerable of attributes to add to the class level and an Enumerable of string/attributes to add to properties. I called this class AttributeTypeDescriptor. Need to add a TypeDescriptionProvider to make sure of this CustomeTypeDescriptor. Nothing to fancy just tell the TypeDescriptor about it and then have a method to add an Attribute to the class and one to add an Attribute to a property. I called this class AttributeProvider.

Here’s where we start to get into the fluent-ish part. I wanted an easy way to configure attributes so I made an interface called IAttributeConfiguration. This interface allows adding property level attributes either by expression or by string if using nameof() and attributes to the class itself. Since it’s generic it already knows what type to add them to. I created the implementation of this called AttributeConfiguration which has a default constructor and one that allows you to replace the provider if you’d like.

Now that we are adding attributes at runtime we are no longer restricted by things needs to be constants when declaring the Attribute. Following the IValidatableObject pattern I was using where each validation was it’s own method I’m going to create a ValidationAttribute that will take a Func. Called this class ValidatableObjectAttribute. Now I need to create a class to handle adding property validation and class validations. Going to skip over some of the implementation details here but is the interfaces/class responsible IPropertyValidations, PropertyValidations, IObjectValidations, ObjectValidations and some extension methods to make accessing them easier.

One of the common things I run into with validations is skipping validation if another property isn’t valid. With that in mind I create a new Validation Attribute called ValidateIfAttribute. This attributes takes a dictionary that the key is the string name of the property and a func of how to get the value of the property and the ValidationAttribute that should be executed if all the other properties are valid.

Word of warning with this you can create a cyclical validations. For Example if FirstName is requiring LastName valid and LastName is requiring FirstName valid. But as a developer I would prefer to have the power with knowledge of the short comings and deal with it then not have the option at all.

In the Property validation there is a setter for if you need to set some properties for the validation attribute that is being added but you also noticed that the setter needs to return an attribute. For the most part returning the attribute that came in back out is what you will want to do but I had it return the attribute for this case. I want to wrap the incoming attribute in the ValidateIfAttribute so that the incoming attribute will only fire if the other properties are valid.

I create an extension method called WhenValid that would return the Func to use to wrap the setter method in. By passing in an Expression in we can get both the name of the property and compile the expression to get the func needed to access the property. I still feel this part is a bit awkward at the moment and would like to see if it can become more fluent.

Now lets go back to the original PO issue. With this solution we can create a class for the validation

public class SalesOrderLineItemsValidations
{
    private readonly IAttributeConfiguration<SalesOrderLineItems> _attributeConfiguration;
    private readonly IDataService _dataService;

    public SalesOrderLineItemsValidations(IAttributeConfiguration attributeConfiguration, IDataService dataService)
    {
        _attributeConfiguration = attributeConfiguration;
        _dataService = dataService;
    }

    public async Task Configure()
    {
        var itemValidations = _attributeConfiguration.ValidationsFor(x => x.Item);
        itemValidations.Add<RequiredAttribute>();

        var itemLength = await _dataService.ItemLengthAsync();
        itemValidations.Add(() => new MaxLengthAttribute(itemLength));

        var qtyValidations = _attributeConfiguration.ValidationsFor(x => x.Qty);
        var whenValid = _attributeConfiguration.WhenValid(x => x.Item);
        qtyValidations.Add(QtyGreaterThanZero, whenValid);
    }

    protected virtual ValidationResult QtyGreaterThanZero(decimal qty)
    {
        if (qty > 0)
        {
            return ValidationResult.Success;
        }

        return new ValidationResult("Quantity must be greater than zero");
    }
}

The Configure method is where the “Fluent-ish” validation happens at. Since I already have a BootStrap class and now I just need to have it add in the validation config classes and execute them.

Last bit of warning/thoughts.

The Validator does cache the attributes on a type the first time it uses the class. Which means if adding validation after the Validator has been called it will not pick up the new validations. Even if there is a way for them to know to refresh it *cough cough*. I don’t see this as a big deal as 99% of the time the validations would be added when the application starts up. I also thought that adding the validation in a different class then the original class might confuse someone coming after me but I feel that’s a training issue and frameworks like Entity Framework have their own configurations and they do allow adding some basic validations so it’s not unheard of. If I need to add a class validation I don’t know if it’s better to just add the IValidatableObject interface or keep all the validation in one class and have the fluent add the validation. I think that’s something the team should decide the pros and cons on.

All the code is hosted in GitHub and is licensed with the MIT license

Wednesday, December 6th, 2017 Validations No Comments

Fluent-ish Validations – Part One (The problem)

A little background. I write software that is then sold to VARs (Value-Added Resellers). They can take our code and enhancement it or change it based on their exact customer needs. We try to write code that allows them to change anything they might need. Now all software does have some basic data it needs to function properly but sometimes we put in ValidationAttribute to just make programming simple. Who doesn’t like ASP.NET MVC or Entity Framework to auto call our validation. But when you add an attribute to a class it’s really hard to take that attribute away or to change it. So typically I’d shy away from ValidationAttribute and instead implement the IValidatableObject Interface. But this leads sometimes to some ugly code.

Let take an arbitrarily contrived example. Maybe not too contrived but it’s simplified down from a real requirement.

User requirement: Create a screen for a user to buy items and needs the ability to enter the item, quantity and date requested.

Rules: The item needs to be filled in and the length of the item is configurable. Quantity must be positive. Date can be empty or any valid date.

Seems simple enough let’s create a class

public class SalesOrderLineItems
{
    public string Item { get; set; }
    public decimal Qty { get; set; }
    public DateTime RequestedBy { get; set; }
}

The business rule of item required is simple we can just slap on the RequiredAttribute but we can’t slap on the MaxLengthAttribute because the length is configurable. Ok we will just handle that with IValidatableObject interface.

To get the item’s length we have a DataService

public interface IDataService
{
    Task ItemLengthAsync();
    int ItemLength();
    Task QuantityPercisionAsync();
    int QuantityPercision();
}code>

Let's write up the validation for max length in the IValidatableObject


public class SalesOrderLineItems : IValidatableObject
{
    [Required]
    public string Item { get; set; }
    public decimal Qty { get; set; }
    public DateTime RequestedBy { get; set; }

    public IEnumerable Validate(ValidationContext validationContext)
    {
        yield return ValidateItem(validationContext);
    }

    /// This method just wraps the MaxLengthAttribute so we can set the length at runtime
    protected virtual ValidationResult ValidateItem(ValidationContext validationContext)
    {
        var dataService = (IDataService)validationContext.ServiceContainer.GetService(typeof(IDataService));
        var maxValidation = new MaxLengthAttribute(dataService.ItemLength());
        var context = new ValidationContext(this, validationContext.ServiceContainer, validationContext.Items)
        {
            MemberName = nameof(Item)
        };
        return maxValidation.GetValidationResult(Item , context);
    }
}

I'm a big fan of constructor injection but not for my models, they should have a default constructor. so we have to go back to using the Service Container (yuck) but it's possible. I also implement each validation in it's own method so the VARs can override a specific validation. I could have made it's own class and using the service locator grab the validation class and call those methods. In that class I could have injected the dataservice but still to get the validation class I need to use the Service Locator pattern. For this example we will leave as methods. For the Qty requirement let's slap on the RangeAttribute [Range(typeof(double),".01","9999999999999999")]

Now we demo to our Product Owner. First complaint is they don't like the range validation error message. Ok we can fix that with a Resource file to change the message. Second complaint is the number of decimals is configured and right now it's hard coded that .01 is the min. Product Owner likes it's trying to verify the minimum quantity that can be entered and not just greater than zero. Hmmm we were thinking of creating a MinAttribute but if they want it to be the min enterable amount based on the dataservice that is out. Product Owner also says they like that the quantity isn't validated if the item hasn't been filled in yet but....they would also like to skip the quantity validation if the item is too long because we know it's not valid. Ok we going to have to create another validation method.

Here's the code that will make our Product Owner happy

public class SalesOrderLineItems : IValidatableObject
{
    [Required]
    public string Item { get; set; }
    public decimal Qty { get; set; }
    public DateTime RequestedBy { get; set; }

    public IEnumerable Validate(ValidationContext validationContext)
    {
        var result =  ValidateItem(validationContext);
        // don't run any other validation if item didn't pass
        if (result == ValidationResult.Success)
        {
            yield return ValidateQty(validationContext);
        }
        else
        {
            yield return result;
        }
    }

    protected virtual ValidationResult ValidateItem(ValidationContext validationContext)
    {
        var dataService = (IDataService)validationContext.ServiceContainer.GetService(typeof(IDataService));
        var maxValidation = new MaxLengthAttribute(dataService.ItemLength());
        var context = new ValidationContext(this, validationContext.ServiceContainer, validationContext.Items)
        {
            MemberName = nameof(Qty)
        };
        return maxValidation.GetValidationResult(Qty, context);
    }

    protected virtual ValidationResult ValidateQty(ValidationContext validationContext)
    {
        var dataService = (IDataService)validationContext.ServiceContainer.GetService(typeof(IDataService));
        var percision = dataService.QuantityPercision();
        var minvalue = Convert.ToDecimal(1/Math.Pow(10, percision));
        if (Qty > minvalue)
        {
            return ValidationResult.Success;
        }
        return new ValidationResult("Quantity must be greater than zero");
    }
}

All that for a simple business requirements the code just keeps expanding if you have more requirements and can quickly get out of hand. We will look at Part 2 how to tame this beast.

Friday, October 27th, 2017 Validations No Comments