Fluent Validations
AutoMapper Mapped Validator
- Given a class
Foo
and another classBar
, who are related but do not inherit from a common parent, we can use AutoMapper to convert from one to another. - When using FluentValidations to validate
Foo
, we may also want to validateBar
. - We need to either create a validator for
Bar
(which may be identical to theFoo
validator), or first mapBar
=>Foo
and then validate. - Using FluentValidation's
Transform()
method, we are able to abide by the DRY rule.
Two classes with no inherited relation
public class Foo
{
string Name { get; set; }
}
public class Bar
{
string Name { get; set; }
}
A validator for one of the classes
public class FooValidator : AbstractValidator<Foo>
{
public FooValidator()
{
RuleFor(f => f.Name)
.NotEmpty()
.WithMessage("Name must not be empty");
}
}
An AutoMapper Profile
public class AutoMapperProfile : Profile
{
public AutoMapperProfile()
{
CreateMap<Foo, Bar>().ReverseMap();
}
}
A 'Mapped Validator'
public class MappedValidator<TSource, TDestination> : AbstractValidator<TSource>
{
public MappedValidator(IMapper mapper, IValidator<TDestination> validator)
{
Transform(from: viewModel => viewModel, to: viewModel => mapper.Map<TSource, TDestination>(viewModel))
.SetValidator(validator);
}
}
Dependency Injection Container Configuration
services
.AddAutoMapper(typeof(AutoMapperProfile))
.AddScoped<IValidator<Foo>, FooValidator>()
.AddScoped<IValidator<Bar>, MappedValidator<Bar, Foo>>()
Usage
- Consumers may now inject either
IValidator<Foo>
orIValidator<Bar>
. - Either injection will use the rules from
FooValidator : AbstractValidator<Foo>
after mappingBar
=>Foo