jeudi 24 août 2017

Validation: Fluentvalidation vs. Reflection - best practise / performance

I have an inventory application that uses an agent installed on a computer to read some information and pass it on to the server for further processing. Also, it will be stored in a database.

Now I wonder about validation of that information (like the name of the computer, operating system, installed software etc).

I do want to prevalidate the data on the agent side to avoid sending data to the server that might harm the database or the later processing classes and cause errors. Of course, I will validate the data on the server side as well, to prevent anyone from sending harmful code to the server. Thus, I don't need a super exact validation on the agent.

Also, to know about validation errors, the data has to be sent to the server in some way anyhow, but I wanted to be sure that I know about that and so can process it differently in the first place. My plan was to send it to the server who just stores it in a log file for further analysis.

I stumbled onto reflection and found that I can use a short piece of code in the marked answer to just validate all integers, strings etc against a regex:

Automate the validation process on properties using FluentValidation Library

public class Validator<T> : AbstractValidator<T>
{
public Validator(Func<PropertyInfo, bool> filter) {
    foreach (var propertyInfo in typeof(T)
        .GetProperties()
        .Where(filter)) {
        var expression = CreateExpression(propertyInfo);
        RuleFor(expression).NotEmpty();
    }
}

private Expression<Func<T, object>> CreateExpression(PropertyInfo propertyInfo) {
    var parameter = Expression.Parameter(typeof(T), "x");
    var property = Expression.Property(parameter, propertyInfo);
    var conversion = Expression.Convert(property, typeof(object));
    var lambda = Expression.Lambda<Func<T, object>>(conversion, parameter);

    return lambda;
}
}

And it can be used as such:

private static void ConfigAndTestFluent()
{
    CustomerDto customer = new CustomerDto();
    Validator<CustomerDto> validator = new Validator<CustomerDto>(x=>x.Name!="Remarks"); //we specify here the matching filter for properties
    ValidationResult results = validator.Validate(customer);
}

I would use that with a differentiation for string/integers etc and be done. But I read that reflection is super slow and should be avoided if possible.

Following JeremySkinner's manual of FluentValidation I would do something as he shows here:

http://ift.tt/1PZO8ta

using FluentValidation;

public class CustomerValidator: AbstractValidator<Customer> {
  public CustomerValidator() {
    RuleFor(customer => customer.Surname).NotEmpty();
   RuleFor(customer => customer.Forename).NotEmpty().WithMessage("Please     specify a first name");
    RuleFor(customer => customer.Discount).NotEqual(0).When(customer =>     customer.HasDiscount);
RuleFor(customer => customer.Address).Length(20, 250);
RuleFor(customer => customer.Postcode).Must(BeAValidPostcode).WithMessage("Please specify a valid postcode");
  }

  private bool BeAValidPostcode(string postcode) {
// custom postcode validating logic goes here
  }
    }

Customer customer = new Customer();
CustomerValidator validator = new CustomerValidator();
ValidationResult results = validator.Validate(customer);

bool validationSucceeded = results.IsValid;
IList<ValidationFailure> failures = results.Errors;

And then iterate over every error, rechecking the property type and the rules that was applied to send that back to the server. The downside is that I have to do validation rules for every single property which I guess is the best practice but is also way more work.

I didn't find any explicit answers to this or maybe I just didn't understand it, so I'd like to ask for some input and help on what to do. Speed actually is not the problem, but I'd like to know what I am doing and what I might be sacrificing in doing so.

Hence, my questions:

  • What's the best practice for validation in my case?
  • Should I go the long route any validate each property separately?
  • Is there any other way of achieving the same goal that might work better?

Any help is greatly appreciated.

Thanks :)





Aucun commentaire:

Enregistrer un commentaire