4

We just received a phase 1 release from a vendor to translate an archaic PowerBuilder application into C# MVC with Angular 5 (we have an Angular guy that has already mostly rewritten the front end in 7 so the security concerns from 5 are a nonissue). Since the statement of work only required them to reproduce the application there are next to zero validations on input because there wasn't much, if any, on the original application.

I have recently done some research into FluentValidation and like it for its reusability later in applications that will use the same overall data. However, looking at this code the models in the MVC are not normalized like they probably should be and so we have dozens of models that likely could be normalized out so that there would be less overlap in data fields such as First Name, Last Name, Address, Business Address etc.

I have basic experience with generics and reflection and have supported a few more advanced examples in the past. So I was trying to find some way to utilze these two concepts to make the validators more dynamic.

I was unable to find much in the way of more advanced FluentValidation examples other than the basic hard connection to a given named model. I have tried to use the generic T in place of the model but was unable to bridge the gap and access the object being passed into the validation.

 public class FormValidator : AbstractValidator<ModelExample>
 {
     public FormValidation()
     {

     }
 }   

//tried to do something like this but wasn't able to access the .HasProperties. Although I was able to access the GetProperties, 
//having trouble implementing it into the RuleFor however.
 public class FormValidation<T> : AbstractValidator<T>
{
    RuleFor(x => x.GetType().GetProperty(nameof({something if it exists}).{check stuff is valid}
{

public class ModelExample
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public DateTime DateOfBirth { get; set; }
}

public class OtherModelExample
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
}

My end goal would be to be able to pass related objects into a given validator and it would be able to determine if the properties existed and act accordingly.

This may be an issue where I don't really know how to ask the question in Google, I tend to have issue wording things in a way where it brings up what I would expect.

This also may not even be possible but if it could save me from writing a series of hard coupled validators that I might have to rewrite later if we ever are allowed to normalize the data flow it would be of great help.

Any articles or documentation with more advanced examples than the simple ones I find would be of great use even beyond this project. Most of the tutorials I find are very basic examples and I sometimes have a hard time picturing them in "real" code application.

Thanks

Slagmoth
  • 173
  • 1
  • 10
  • Have you had any breakthroughs? I am stuck as well. I've tried using all kinds of tricks and hacks (i.e. ExpandoObject, class indexors...etc). But no luck. – AlvinfromDiaspar Aug 07 '19 at 03:31
  • 1
    @AlvinfromDiaspar I haven't implemented it and got side-tracked with another project at work but I did get what amounted to a helper/wrapper that you pass in the type and name you needed to validate and it would take care of it for you. I had planned on posting it as soon as I proved it worked the way I saw it in my head, but deadlines have gotten in the way thus far. It is probably not an ideal solution but saves a bit of coding in each controller. – Slagmoth Aug 07 '19 at 11:32
  • @Stagmoth - I was able to find a workable, generic solution. I paraphrased my steps here. https://stackoverflow.com/questions/57151623/using-fluentvalidation-to-validate-on-a-collection-of-fields-rather-than-on-a-v – AlvinfromDiaspar Aug 07 '19 at 18:46

1 Answers1

1

Instead of creating generic validators for a whole model, have you considered the reverse and creating them for each property?

If you use custom property validators you can specify the validator logic once, and then simply create a validator class per view model.

eg:

class Program
{
    static void Main(string[] args)
    {
        var person = new Person
        {
            Name = "Ada",
            NickName = "A"
        };
        var validator = new PersonValidator();
        var result = validator.Validate(person);

        //Should be a problem with the NickName
    }
}
class Person
{
    public string Name { get; set; }
    public string NickName { get; set; }
}

class PersonValidator : AbstractValidator<Person>
{
    public PersonValidator()
    {
        RuleFor(x => x.Name).SetValidator(new NameValidator());
        RuleFor(x => x.NickName).SetValidator(new NameValidator());
    }
}

public class NameValidator : AbstractValidator<string>
{
    public NameValidator()
    {
        RuleFor(x => x).Must(x => x.Length > 1)
            .WithMessage("The name is not long enough");
    }
}

This is probably a safer option too, as it's opt in rather than implicit.

AlexH
  • 115
  • 13
  • Doesn't this still require me to make a validator for each model though? That or instantiate a ton of classes to validate all these fields? Maybe I am failing to connect the dots to my initial problem. – Slagmoth Feb 08 '19 at 18:00
  • Yes it does, though a way around that is to use composition, eg: if person has an address, rather than having a flat view model with all of the address properties, have the address wrapped up in a separate class. There's another alternative involving interfaces that might work (eg: specify properties via an interface per class), but iirc it requires a custom validator factory and is quite a bit more complex to implement.. it also almost certainly wont work out of the box with any client side validation. – AlexH Feb 10 '19 at 11:46
  • Thanks, I suppose I can use AutoFac to condense some of the instantiation doing the building from the entire folder using reflection for all AbstractValidator objects. I will look into that to lighten the explict calls throughout. – Slagmoth Feb 11 '19 at 14:56