Bee Pee

Validating entities using Fluent Validation Library.

Problem: I have set of entities which are being used in service. And i need to validate them for following condition.
Property is mandatory
Property is a string of length 30
Property has value  either “PSHP” or “STOR”
If property in point 4 has value then another property also as a value.

Solution : The idea is to use Fluent Library nuget package
PM>  Install-Package FluentValidation
So our entity is as below

public class Parcel
    {
        public string OrderNumber { get; set; }
        public string Description { get; set; }
        public string NodeType { get; set; }
        public int NodeId { get; set; }
        public string NodeName { get; set; }
        public Address Address { get; set; }
    }

OrderNumber is mandatory
Description Not mandatory but can be of maximum length 30
NodeType can have value of “PSHP” or “STOR”
if NodeType id supplied then NodeId and NodeName should also be supplied.
Create Class lets call it ParcelValidator after our entity Parcel.
This class will implement generic class called  AbstractValidator.
Now lets see how we can implement the mandatory behaviour.
Rule 1

public ParcelValidator()
{
    RuleFor(parcel => parcel.OrderNumber).Length(1, 15).NotEmpty().WithMessage("Order Number is required");
}

Simples!!!

You can even extract the message from resource file if you want.

Rule no 2:

RuleFor(parcel => parcel.Description).Length(0, 30).WithMessage("Description can be only 30 characters");

The fact that we say length from 0, 30 makes it only 30 characters.

Rule no 3 & 4:

 When(parcel => !string.IsNullOrEmpty(parcel.NodeType), () =>
  {
       RuleFor(parcel => parcel.NodeType).Length(0, 4).WithMessage("Invalid Node type");
       RuleFor(parcel => parcel.NodeType).Must(BeAValidNodetype).WithMessage("Invalid Node type");
       RuleFor(parcel => parcel.NodeId).NotEmpty().WithMessage("Nodeid is required");
       RuleFor(parcel => parcel.NodeName).NotEmpty().WithMessage("Node name is required");
           
    });

Since we want to check the properties in rule 3 & 4 only if NodeType is provided we need to use the When, Unless construct.
Again since we want to check if the node type is “PSHP” or “STOR” we will have private method which must be obeyed. Again this check is implemented by using the keyword Must() and passing the name of the method.
Lets Look at the code all together.

public class ParcelValidator : AbstractValidator<Parcel>
    {
 
        public ParcelValidator()
        {
            RuleFor(parcel => parcel.OrderNumber).Length(1, 15).NotEmpty().WithMessage("Order Number is required");
            RuleFor(parcel => parcel.Description).Length(0, 30).WithMessage("Description can be only 30 characters");
            RuleFor(parcel => parcel.Address.HouseNo).NotEmpty().WithMessage("House Number is required");
            When(parcel => !string.IsNullOrEmpty(parcel.NodeType), () =>
            {
                RuleFor(parcel => parcel.NodeType).Length(0, 4).WithMessage("Invalid Node type");
                RuleFor(parcel => parcel.NodeType).Must(BeAValidNodetype).WithMessage("Invalid Node type");
                RuleFor(parcel => parcel.NodeId).NotEmpty().WithMessage("Nodeid is required");
                RuleFor(parcel => parcel.NodeName).NotEmpty().WithMessage("Node name is required");
            });
 
 
        }
 
        private bool BeAValidNodetype(string nodeType)
        {
            if (string.IsNullOrEmpty(nodeType))
                return true;
            if ((nodeType == "PSHP") || (nodeType == "STOR"))
            {
                return true;
            }
            return false;
        }
    }

Conclusion: The fluent validation library is very easy to implement the rules especially the rule number 4. Also, what is not apparent from the set of rule is that i am using the same entity for different clients and they have different set of rules in terms of length and whether or not the property is mandatory.
Complete project is available for download at GitHub

Leave a Reply

Your email address will not be published. Required fields are marked *