FluentValidation - pre-validation / conditional validation with no code duplication - c#

I'm trying to create Validation which is able to have two groups and block second validation if first fail (it contains many rules).
For now I did create a private 'BasicValidation' class inside and in 'main validator' do sth like this:
RuleFor(m => m).SetValidator(new BasicValidation()).DependentRules(() => {
//Complex validation
RuleFor(m => m.IdOfSthInDb)
.MustAsync(ItemMustExists)
.WithMessage("Item does not exist.");
});
It does the trick but I would like to avoid creating that 'BasicValidation' for each model.

In my previous answer I misunderstood the question. The main goal is to avoid code duplication in different validators. After some investigation I found solution that matches your requirements. Suppose you have models:
public abstract class BaseModel
{
public string BaseProperty1 { get; set; }
public string BaseProperty2 { get; set; }
}
public class ChildModel : BaseModel
{
public int IdOfSthInDb { get; set; }
}
You have to create validator for base model (it would be used further):
class InternalBaseModelValidator : AbstractValidator<BaseModel>
{
public InternalBaseModelValidator()
{
RuleFor(x => x.BaseProperty1).NotEmpty().WithMessage("Property 1 is empty");
RuleFor(x => x.BaseProperty2).NotEmpty().WithMessage("Property 2 is empty");
}
}
Then you can use new feature of FluentValidation, called PreValidate:
public class BaseModelValidator<T>: AbstractValidator<T> where T : BaseModel
{
// necessary for reusing base rules
private readonly InternalBaseModelValidator preValidator;
protected BaseModelValidator()
{
preValidator = new InternalBaseModelValidator();
}
protected override bool PreValidate(ValidationContext<T> context, ValidationResult result)
{
var preValidationResult = preValidator.Validate(context.InstanceToValidate);
if (preValidationResult.IsValid)
{
return true;
}
foreach(var error in preValidationResult.Errors)
{
result.Errors.Add(new ValidationFailure(error.PropertyName, error.ErrorMessage, error.AttemptedValue));
}
return false;
}
}
After creating validator for all base models you can inherit from it for ChildModel validation:
public class ChildModelValidator : BaseModelValidator<ChildModel>
{
public ChildModelValidator()
: base()
{
RuleFor(x => x.IdOfSthInDb)
.MustAsync(ItemMustExists)
.WithMessage("Item does not exist.");
}
private Task<bool> ItemMustExists(int arg1, CancellationToken arg2)
{
return Task.FromResult(false); // some logic here
}
}
That's it!

I think hext code would solve your problem:
var basicValidator = new BasicValidation();
RuleFor(m => m).SetValidator(basicValidator));
When(m => basicValidator.Validate(m).IsValid, () =>
{
RuleFor(m => m.IdOfSthInDb)
.MustAsync(ItemMustExists)
.WithMessage("Item does not exist.");
});

Related

Cannot implicitly convert A<B> to A<C<D>> where B inherit from C<D>

I'm building a sort of library to perform text replacement in a document based on some rule. We built a POC and now I'm trying to create a library as generic as possible.
I have just one problem with inheritance:
This is the simplified representation of the classes/interfaces I'm dealing with:
public interface IRule {}
public interface IReplaceRule<T> : IRule
{
T ReplaceValue { get; set; }
}
public class CachedRules<T> where T : IReplaceRule<object>
{
#region Props
public T RuleTemplate { get; set; }
public IDictionary<string, T> RuleList { get; private set; } = null;
#endregion
public void SetRuleList(IDictionary<string, T> ruleList) { ... }
public bool ContainsRuleByKey(string key) { ... }
public bool TryGetRuleValueByKey(string key, out T rule) { ... }
}
public class SingleRowRule : IReplaceRule<string> { ... }
I also have a class which is like a repository of rules, and inside it I can add as many CachedRules as I need:
public class RulesStorage : AbstractRulesStorage
{
private CachedRules<SingleRowRule> singleRowRules;
public RulesStorage() { ... }
// Bunch of methods not useful for this question
// Here I need to return a list of ChachedRule, but just ofr testing I tried to return only one
public CachedRules<IReplaceRule<object>> GetCachedReplaceRules()
{
return singleRowRules;
}
}
Inside this class I need a method to return all the CachedRules declared in the RulesStorage:
Unfortunately the RulesStorage.GetCachedReplaceRules method give me this error:
Cannot implicitly convert type TestLib.Model.CachedRules<TestLib.Rules.SingleRowRule> to TestLib.Model.CachedRules<TestLib.Abstractions.IReplaceRule<object>
I really don't like the fact that I had to put <object> since IReplaceRule requires a generic and also I'm stuck because I don't know how to return this list of CachedRules without getting this compilation error.
Do you have some idea? Do I have to organize the code differently in your opinion?
Hope I've made myself clear and thanks in advance!
Instead of doing IReplaceRule<object> you can do it the way IEnumerable<T> inherits from IEnumerable. With that minor tweak in place, I create an implicit converter to go from T to IReplaceRule and the constraint in place now ensures I can actually do this safely.
I'm assuming you have a reason to have private CachedRules<SingleRowRule> singleRowRules; and can't just using private CachedRules<IReplaceRule> singleRowRules; which would remove the need for this extra conversion hop.
Code:
public interface IReplaceRule : IRule { object ReplaceValue { get; set; } }
public interface IReplaceRule<T> : IReplaceRule { new T ReplaceValue { get; set; } }
public class CachedRules<T> where T : IReplaceRule
{
public IDictionary<string, T> RuleList { get; private set; } = new Dictionary<string, T>();
//The key ingredient for a nice experience instead of just doing this in the method
public static implicit operator CachedRules<IReplaceRule>(CachedRules<T> rules)
=> new CachedRules<IReplaceRule> { RuleList = rules.RuleList.ToDictionary(x => x.Key, x => x.Value as IReplaceRule) };
}
public class SingleRowRule : IReplaceRule<string>
{
public string ReplaceValue { get; set; }
object IReplaceRule.ReplaceValue { get => ReplaceValue; set => ReplaceValue = value as string; }
}
public class RulesStorage
{
private CachedRules<SingleRowRule> singleRowRules = new CachedRules<UserQuery.SingleRowRule>();
//FIXME: just for testing purposes
public RulesStorage() => singleRowRules.RuleList.Add("Hello", new SingleRowRule { ReplaceValue = "World" });
// Here I need to return a list of ChachedRule, but just ofr testing I tried to return only one
public CachedRules<IReplaceRule> GetCachedReplaceRules() => singleRowRules;
}

Automapper - Add to List conditionally

The situation is -
public class One
{
public string S1 { get;set; }
public string S2 { get;set; }
public string S3 { get;set; }
public string S4 { get;set; }
}
public class Two
{
public List<string> List1 { get;set; }
}
Now what I want is to populate the list inside Two with the non-null property values of One.
Is there anyway / work-around to achieve this using AutoMapper ?
In this case you can create your own custom value resolver:
public class CustomResolver : ValueResolver<One, List<string>>
{
protected override List<string> ResolveCore(One source)
{
var result = new List<string>();
//your logic
if (!string.IsNullOrWhiteSpace(source.S1))
result.Add(source.S1);
if (!string.IsNullOrWhiteSpace(source.S2))
result.Add(source.S2);
if (!string.IsNullOrWhiteSpace(source.S3))
result.Add(source.S3);
if (!string.IsNullOrWhiteSpace(source.S4))
result.Add(source.S4);
return result;
}
}
Mapping configuration:
Mapper.CreateMap<One, Two>()
.ForMember(dest => dest.List1, opt => opt.ResolveUsing<CustomResolver>());
In this way you are able to configure mappings for specific properties
If that is the only mapping you want to do between the classes, you can use a custom type converter: this then takes complete charge of the mapping between the objects.
public class OneTwoTypeResolver : TypeConverter<One, Two>
{
protected override Two ConvertCore(One source)
{
Two two = new Two {List1 = new List<string>()};
if (source.S1 != null)
two.List1.Add(source.S1);
if (source.S2 != null)
two.List1.Add(source.S2);
if (source.S3 != null)
two.List1.Add(source.S3);
if (source.S4 != null)
two.List1.Add(source.S4);
return two;
}
}
You tell AutoMapper to use this class when mapping between the classes:
Mapper.CreateMap<One, Two>().ConvertUsing<OneTwoTypeResolver>();
Then this test will pass:
public void Test()
{
One one = new One {S2 = "OneTwoThreeFour"};
Two two = Mapper.Map<One, Two>(one);
Assert.AreEqual(1, two.List1.Count);
Assert.AreEqual("OneTwoThreeFour", two.List1.Single());
}

FluentValidation SetCollectionValidator for derived types

How can I set validators on a collection items of derived types?
class BaseClass
{
}
class DerivedClass : BaseClass
{
}
class SomeClass
{
public IEnumerable<BaseClass> BaseClasses { get; set; }
}
class DerivedClassValidator : AbstractValidator<DerivedClass>
{
}
class SomeClassValidator : AbstractValidator<SomeClass>
{
public SomeClassValidator()
{
RuleFor(x => x.BaseClasses).????.SetCollectionValidator(new DerivedClassValidator);
}
}
Just wondering...
Is there a way to cast it to a particular type like
RuleFor(x => x.SomeCollection).CastTo(typeof(SomeDerivedType)).SetCollectionValidator(new SomeDerivedValidator());
You can use conditional wrapping of rules to validate collection, which contains objects of different derived types.
Suppose you have next class hierarchy:
public class BaseClass
{
public string Name { get; set; }
}
public class DerivedClassOne : BaseClass
{
public int Count { get; set; }
}
public class DerivedClassTwo : BaseClass
{
public double Price { get; set; }
}
And container class with collection of BaseClass objects:
public class ContainerClass
{
public List<BaseClass> Collection { get; set; }
}
Main idea is to create one validator class, that responsible for all class hierarchy validation:
public class CommonBaseClassValidator : AbstractValidator<BaseClass>
{
public CommonBaseClassValidator()
{
//common rule for all BaseClass types
RuleFor(x => x.Name)
.NotEmpty();
// special rules for base type
When(model => model.GetType() == typeof (BaseClass), () =>
{
RuleFor(x => x.Name)
.Length(0, 10);
// add rules here
});
//special rules for derived types
When(model => model.GetType() == typeof(DerivedClassOne), () =>
{
RuleFor(model => ((DerivedClassOne) model).Count)
.ExclusiveBetween(1, 9);
// add rules here
});
When(model => model.GetType() == typeof(DerivedClassTwo), () =>
{
RuleFor(model => ((DerivedClassTwo) model).Price)
.GreaterThan(1000);
// add rules here
});
}
}
And register this class as a collection item validator:
public class ContainerValidator : AbstractValidator<ContainerClass>
{
public ContainerValidator()
{
RuleFor(model => model.Collection)
.SetCollectionValidator(new CommonBaseClassValidator());
}
}
I created this to make dealing with this scenario simpler:
public class DerivedValidatorBase<TBase> : AbstractValidator<TBase>
{
public void MapDerivedValidator<TType, TValidatorType>()
where TValidatorType : IEnumerable<IValidationRule>, IValidator<TType>, new()
where TType: TBase
{
When(t => t.GetType() == typeof(TType), () => AddDerivedRules<TValidatorType>());
}
public void MapDerivedValidator<TType, TValidatorType>(TValidatorType validator)
where TValidatorType : IEnumerable<IValidationRule>, IValidator<TType>
where TType: TBase
{
When(t => t.GetType() == typeof(TType), () => AddDerivedRules<TValidatorType>(validator));
}
private void AddDerivedRules<T>(T validator)
where T : IEnumerable<IValidationRule>
{
foreach (var rule in validator)
{
this.AddRule(rule);
}
}
private void AddDerivedRules<T>()
where T : IEnumerable<IValidationRule>, new()
{
IEnumerable<IValidationRule> validator = new T();
foreach (var rule in validator)
{
this.AddRule(rule);
}
}
}
Then I simply create a base validator class:
public class CommonBaseClassValidator : DerivedValidatorBase<BaseClass>
{
public CommonBaseClassValidator()
{
MapDerivedValidator<DerivedClass, DerivedClassValidator>();
}
}
or when using dependency injection:
public class CommonBaseClassValidator : DerivedValidatorBase<BaseClass>
{
public CommonBaseClassValidator(DerivedClassValidator validator)
{
MapDerivedValidator<DerivedClass, DerivedClassValidator>(validator);
}
}
In use:
RuleFor(v => v.BaseClasses).SetCollectionValidator(new CommonBaseClassValidator());
That way I can re-use existing validators for the derived classes, which I may be using elsewhere, and map them without hassle.
I'd like to add something to help others. I was inspired by Evgeny Levin solution.
Instead of a list of rules within When I preferred to separate each validator.
public class CommonBaseClassValidator : AbstractValidator<BaseClass>
{
public CommonBaseClassValidator()
{
//All rules for shared properties
RuleFor(x => x.Name)
.NotEmpty();
RuleFor(x => x.Name)
.Length(0, 10);
//special rules for derived types
When(model => model.GetType() == typeof(DerivedClassOne),
() => RuleFor(entity => entity as DerivedClassOne)
.SetValidator(new DerivedClassOneValidator()));
When(model => model.GetType() == typeof(DerivedClassTwo),
() => RuleFor(entity => entity as DerivedClassTwo)
.SetValidator(new DerivedClassTwoValidator()));
}
}
Hope that helps.
After 6 years: please have a look at this link.
Follow the instructions, and at the bottom it says:
This method also works with collections, where each element of the collection may be a different subclass.

FluentValidation rule for null object

I've been trying to work out how to create a FluentValidation rule that checks if the instance of an object it's validating is not null, prior to validating it's properties.
I'd rather encapsulate this null validation in the Validator rather then doing it in the calling code.
See example code below with comments where the required logic is needed:
namespace MyNamespace
{
using FluentValidation;
public class Customer
{
public string Surname { get; set; }
}
public class CustomerValidator: AbstractValidator<Customer>
{
public CustomerValidator()
{
// Rule to check the customer instance is not null.
// Don't continue validating.
RuleFor(c => c.Surname).NotEmpty();
}
}
public class MyClass
{
public void DoCustomerWork(int id)
{
var customer = GetCustomer(id);
var validator = new CustomerValidator();
var results = validator.Validate(customer);
var validationSucceeded = results.IsValid;
}
public Customer GetCustomer(int id)
{
return null;
}
}
}
So my question is how do I check in the CustomerValidator() constructor that the current instance of customer is not null and abort further rule processing if it is null?
Thanks in advance.
EDIT 2022-07-19
As some commenters have pointed out, please check out answer https://stackoverflow.com/a/52784357/1943 for a newer implementation. I haven't personally vetted, but it's worth a try to give that a go first.
If you're using an older version, or you enjoy nostalgia, my original answer below is from 2013.
You should be able to override the Validate method in your CustomerValidator class.
public class CustomerValidator: AbstractValidator<Customer>
{
// constructor...
public override ValidationResult Validate(Customer instance)
{
return instance == null
? new ValidationResult(new [] { new ValidationFailure("Customer", "Customer cannot be null") })
: base.Validate(instance);
}
}
I can't really test that right now, but you can either try to override Validate, or include the rules in the When block:
public CustomerValidator()
{
When(x => x != null, () => {
RuleFor(x => x.Surname).NotEmpty();
//etc.
});
}
For those using version >6.2.1 you need to override this signature instead, in order to achieve the same as #chrispr:
public override ValidationResult Validate(ValidationContext<T> context)
{
return (context.InstanceToValidate == null)
? new ValidationResult(new[] { new ValidationFailure("Property", "Error Message") })
: base.Validate(context);
}
/// EXAMPLE FOR NETCORE-3.1
/// fluentvalidator-9.5.0
public class Organisation
{
public string Name { get; set; }
}
public class OrganisationValidator : AbstractValidator<Organisation>
{
public OrganisationValidator()
{
RuleFor(x => x.Name).NotNull().MaximumLength(50);
}
protected override bool PreValidate(ValidationContext<Organisation> context, ValidationResult result)
{
if (context.InstanceToValidate == null) {
result.Errors.Add(new ValidationFailure("", "org is null"));
return false;
}
return base.PreValidate(context, result);
}
}
[TestClass]
public class UnitTest1
{
[TestMethod]
public void ValidateWithNull()
{
var validator = new OrganisationValidator();
Organisation organisation = null;
var result = validator.Validate(organisation);
// result.Errors[0].ErrorMessage == "org is null";
}
}
This is an older post, but want to update the answers to include the following from the FluentValidation documentation:
Using PreValidate
If you need to run specific code every time a validator is invoked, you can do this by overriding the PreValidate method. This method takes a ValidationContext as well as a ValidationResult, which you can use to customise the validation process.
public class MyValidator : AbstractValidator<Person> {
public MyValidator() {
RuleFor(x => x.Name).NotNull();
}
protected override bool PreValidate(ValidationContext<Person> context, ValidationResult result) {
if (context.InstanceToValidate == null) {
result.Errors.Add(new ValidationFailure("", "Please ensure a model was supplied."));
return false;
}
return true;
}
}
https://docs.fluentvalidation.net/en/latest/advanced.html?#prevalidate
I inherited from the fluent AbstractValidator and created a NullReferenceAbstractValidator class instead:
public class NullReferenceAbstractValidator<T> : AbstractValidator<T>
{
public override ValidationResult Validate(T instance)
{
return instance == null
? new ValidationResult(new[] { new ValidationFailure(instance.ToString(), "response cannot be null","Error") })
: base.Validate(instance);
}
}
and then inherited from that class with each validator that needed a null reference check:
public class UserValidator : NullReferenceAbstractValidator<User>
As the above solutions didn't work for me (FluentValidation, Version=6.2.1.0 for Net45), I am posting what I did.
This is just a simple replacement/wrapper for ValidateAndThrow extension method.
public static class ValidatorExtensions
{
public static void ValidateAndThrowNotNull<T>(this IValidator<T> validator, T instance)
{
if (instance == null)
{
var validationResult = new ValidationResult(new[] { new ValidationFailure("", "Instance cannot be null") });
throw new ValidationException(validationResult.Errors);
}
validator.ValidateAndThrow(instance);
}
}
By means of Custom(). It can be also very helpful when validation of another field is based on validation of your current field.
ruleBuilder.Custom((obj, context) =>
{
if (obj != null)
{
var propertyName = <field where should be validation>;
context.AddFailure(propertyName, "'Your field name' Your validation message.");
}
});
Use the Cascade mode.
Here is the example from the documentation.
RuleFor(x => x.Surname).Cascade(CascadeMode.StopOnFirstFailure).NotNull().NotEqual("foo");
Also from the documentation:
If the NotNull validator fails then the NotEqual validator will not be
executed. This is particularly useful if you have a complex chain
where each validator depends on the previous validator to succeed.
Override EnsureInstanceNotNull as below
protected override void EnsureInstanceNotNull(object instanceToValidate)
{
if(instanceToValidate==null)
throw new ValidationException("Customer can not be null");
}
You can override a virtual method called EnsureInstanceNotNull as the author recommends here
public class CustomerValidator: AbstractValidator<Customer>
{
public CustomerValidator()
{
// Rule to check the customer instance is not null.
RuleFor(c => c).NotNull();
// Don't continue validating.
RuleFor(c => c.Surname).NotEmpty();
}
protected override void EnsureInstanceNotNull(object instance) { }
}
The commonly accepted PreValidate answer will not work in this instance, as per here:
If you use SetValidator with an AbstractValidator derivative, then it
will not be run if the property value is null. This is intentional
behaviour as AbstractValidator derivatives are designed to validate
the properties of a complex type, which cannot be done if the instance
is null. AbstractValidator is not designed for use with
simple/primative types or object. Now, if you want to check for null
here, then you can precede the SetValidator call with a NotNull rule
(although it doesn't seem like this is what you want in this case).
For me the only thing that worked is (my example):
RuleForEach(command => command.ProductDto.ProductInfos).NotNull().WithMessage("Custom Message").SetValidator(new ProductInfoValidator());
Without the "NotNull()", null values will be skipped.

Unit testing FluentValidation rules for classes with sub/child classes

Is it possible to write unit tests for fluentvalidation classes when the object we are validating has child classes that are also being validated.
As an example:
My class looks like this
public class TestModel
{
public class SubData
{
public int SubDataVal { get; set; }
}
public int ParentVal { get; set; }
public SubData Sub { get; set; }
}
My validation logic looks like this:
public class TestModelValidator : AbstractValidator<TestModel>
{
public TestModelValidator()
{
RuleFor(o => o.ParentVal).GreaterThan(0);
RuleFor(o => o.Sub.SubDataVal).GreaterThan(0);
}
}
And when I write the following unit test
[Test]
public void Should_have_error_when_val_is_zero()
{
validator = new TestModelValidator();
validator.ShouldHaveValidationErrorFor(model => model.ParentVal, 0);
}
I get a "System.NullReferenceException : Object reference not set to an instance of an object." exception from
FluentValidation.TestHelper.ValidatorTester`2.ValidateError(T instanceToValidate)
(if I remove the RuleFor(o => o.Sub.SubDataVal).GreaterThan(0); line, then it works!)
Similarly if I try and unit test the actual child class with:
[Test]
public void Should_have_error_when_sub_dataVal_is_zero()
{
validator = new TestModelValidator();
validator.ShouldHaveValidationErrorFor(model => model.Sub.SubDataVal, 0);
}
I get a "System.Reflection.TargetException : Object does not match target type." from FluentValidation.TestHelper.ValidatorTester`2.ValidateError(T instanceToValidate)
You can unit test models and child models but you will need to change your validation class to use a separate validator class which just validates the child model:
public class TestModelValidator : AbstractValidator<TestModel>
{
public TestModelValidator()
{
RuleFor(o => o.ParentVal).GreaterThan(0);
RuleFor(o => o.Sub).SetValidator(new SubDataValidator());
}
}
public class SubDataValidator : AbstractValidator<SubData>
{
public SubDataValidator()
{
RuleFor(o => o.SubDataVal).GreaterThan(0);
}
}
You can then write your unit tests to test each validator or both together.
I have come to the conclusion that for this ShouldHaveValidationErrorFor is just not capabable of dealing with subclasses, so have resorted to doing it manually. i.e.
[Test]
public void Should_have_error_when_val_is_zero()
{
validator = new TestModelValidator();
TestModel testRequest = new TestModel();
//populate with dummy data
var result = validator.Validate(testRequest);
Assert.That(result.Errors.Any(o => o.PropertyName== "ParentVal"));
}
With MSTest and FluentAssertions you can write
[TestMethod]
public void Should_have_error_when_val_is_zero()
{
// Given
var validator = new TestModelValidator();
var testModel = TestModel
{
ParentVal = 0
}; // You should create a invalid TestModel object here
// When
validator.Validate(testModel).IsValid.Should().BeFalse();
}
For anyone getting here as I did.
As all rules are executed during the Validation you need to set the whole model to reasonable values.
With current version you can do following:
//using FluentValidation;
//using FluentValidation.TestHelper;
[Fact]
public void Should_have_error_when_val_is_zero()
{
var validator = new TestModelValidator();
var model = new TestModel()
{
ParentVal = 0,
Sub = new TestModel.SubData()
};
var result = validator.TestValidate(model);
result.ShouldHaveValidationErrorFor(model => model.ParentVal);
//result.ShouldHaveValidationErrorFor(nameof(TestModel.ParentVal));
}

Categories