81

I have a hierarchy of data classes

public class Base
{
    // Fields to be validated
}

public class Derived1 : Base
{
    // More fields to be validated
}

public class Derived2 : Base
{
    // More fields to be validated
}

What would be the appropriate way to validated Derived1 and Derived2 using FluentValidation framework without duplicating rules for fields of Base class?

Nikolay Nahimov
  • 810
  • 1
  • 6
  • 6

3 Answers3

133
public class Derived2Validator : AbstractValidator<Derived2>
{
    public Derived2Validator()
    {
        Include(new BaseValidator());
        Include(new Derived1Validator());
        RuleFor(d => d.Derived1Name).NotNull();
    }
}

Derived2Validator does not need to inherit BaseValidator or Derived1Validator.

Use the Include method to include the rules from other validators.

Nechemia Hoffmann
  • 2,118
  • 2
  • 12
  • 21
Mohsin Syed
  • 1,449
  • 1
  • 9
  • 6
92

One approach to take would be as follows:

public class Base
{
    public string BaseName { get; set; } 
}

public class Derived1 : Base
{
    public string Derived1Name { get; set; }
}

public class BaseValidator<T> : AbstractValidator<T> where T : Base
{
    public BaseValidator()
    {
        RuleFor(b => b.BaseName).NotNull();
    }
}

public class Derived1Validator : BaseValidator<Derived1>
{
    public Derived1Validator()
    {
        RuleFor(d => d.Derived1Name).NotNull();
    }
}

So you first create your base validator, make it accept a generic type argument and specify that the generic type must be of type base. Set up your general rules for your base class and move on.

For any validators that validate children of your base class, you have those validators inherit from the baseValidator, where T will be your derived class type.

Yannick Meeus
  • 5,643
  • 1
  • 35
  • 34
  • 3
    Thanks for the answer! – Nikolay Nahimov Aug 14 '15 at 12:16
  • Perfect! Works like a charm! Noticed that there is no need to add : base() after the derived constructor. Initially, I had though it wont pick up the base tests without explicitly calling the base constructor. But it does! – Rashmi Pandit Feb 04 '16 at 05:36
  • You do not need to add base() if you have no constructors, because it is automatically there. If you had other constructors, apart from the default empty, then you must use base() using the signature you need. On the validation classes.. probably you will never need to add any other different constructors. – Piotr Kula Jun 09 '17 at 07:36
  • Exactly what I needed. Thank you very much! It's working perfectly. – Andrei Bazanov Jun 28 '19 at 13:58
  • 2
    What if I have a `ICollection` now and want to validate it? – julealgon Dec 06 '19 at 20:25
  • 1
    I noticed with this approach the validation works on the server-side but the `data` attributes on the client-side don't get rendered with jquery unobtrusive validation so I had to use the `Include` method instead. – Rami A. Oct 25 '20 at 06:42
2

I tried the Include() method, but that did not give me desired results as models generated by swagger in .net core did not show any changes. what did work was creating a new class to inherit from for validators that have a base class

/// <summary>
/// Base Class for entity validator classes that specifies a base validator class
/// </summary>
/// <typeparam name="T">The Type being validated</typeparam>
/// <typeparam name="TBaseClass">The validater assigned to the base type of the type being validated</typeparam>
public abstract class BaseAbstractValidator<T, TBaseClass> : AbstractValidator<T>
    where TBaseClass : IEnumerable<IValidationRule>
{
    protected BaseAbstractValidator() => AppendRules<TBaseClass>();

    /// <summary>
    /// Add the set of validation rules
    /// </summary>
    /// <typeparam name="TValidationRule"></typeparam>
    private void AppendRules<TValidationRule>() where TValidationRule : IEnumerable<IValidationRule>
    {
        var rules = (IEnumerable<IValidationRule>)Activator.CreateInstance<TValidationRule>();
        foreach (var rule in rules)
        {
            AddRule(rule);
        }
    }
}
jonmeyer
  • 748
  • 8
  • 22