I have 2 classes in model and I would like to validate that value in a field from one class is smaller than field from second class. I went through Fluent documentation but I cannot find a real example.
public class inputModel
{
public double Iy { get; set; }
public bool selfWeight { get; set; }
public double span { get; set; }
public string spanType { get; set; }
}
public class pointLoad
{
public double pLoad { get; set; }
public double pDist { get; set; }
}
I need to validate that field pDist cannot be greater than span.
public class pointLoadFluentValidator : AbstractValidator<pointLoad>
{
public pointLoadFluentValidator()
{
RuleFor(x => x.pLoad)
.NotEmpty()
.NotNull()
.LessThan(9999);
RuleFor(x => x.pDist)
.NotEmpty()
.NotNull()
.LessThan(99);
}
Thanks.
Assuming that you're using the latest version of FV, and there is no way to establish a relationship between the models, I'd normally tackle this using RootDataContext. You haven't stated how you're invoking your validators, however this method becomes a server side invocation - you'll need to invoke the validator manually as you need to populate the root data context dictionary.
Provided the above suits, then start by adding a rule:
RuleFor(x => x.pDist).Must((pointLoad, pDist, validationContext) =>
{
if (!validationContext.RootContextData.ContainsKey("inputModelToCompareAgainst"))
{
return true;
}
var inputModel = (inputModel)validationContext.RootContextData["inputModelToCompareAgainst"];
return pDist <= inputModel.span;
})
.WithMessage("My error message");
Then invoke the validator with a validation context:
var validator = new pointLoadFluentValidator();
var validationContext = new ValidationContext<pointLoad>(pointLoad);
validationContext.RootContextData["inputModelToCompareAgainst"] = inputModel;
var validationResult = validator.Validate(validationContext);
Working LINQPad example (with examples for Custom
and Must
validators):
void Main()
{
var fixture = new Fixture();
var pointLoad = fixture.Build<pointLoad>().With(x => x.pLoad, 10).With(x => x.pDist, 20).Create();
var inputModel = fixture.Build<inputModel>().With(x => x.span, pointLoad.pDist - 1).Create();
var validator = new pointLoadFluentValidator();
var validationContext = new ValidationContext<pointLoad>(pointLoad);
validationContext.RootContextData["inputModelToCompareAgainst"] = inputModel;
var validationResult = validator.Validate(validationContext);
validationResult.Errors.Select(x => x.ErrorMessage).Should().BeEquivalentTo(new[] { "My error message" });
}
// You can define other methods, fields, classes and namespaces here
public class inputModel
{
public double Iy { get; set; }
public bool selfWeight { get; set; }
public double span { get; set; }
public string spanType { get; set; }
}
public class pointLoad
{
public double pLoad { get; set; }
public double pDist { get; set; }
}
public class pointLoadFluentValidator : AbstractValidator<pointLoad>
{
public pointLoadFluentValidator()
{
RuleFor(x => x.pLoad)
.NotEmpty()
.NotNull()
.LessThan(9999);
RuleFor(x => x.pDist)
.NotEmpty()
.NotNull()
.LessThan(99);
//pointLoad.pDist cannot be greater than inputModel.span, therefore pointLoad.pDist must be less than or equal to inputModel.span
//RuleFor(x => x.pDist).Custom((pDist, validationContext) =>
//{
// if (validationContext.RootContextData.ContainsKey("inputModelToCompareAgainst"))
// {
// var inputModel = (inputModel)validationContext.RootContextData["inputModelToCompareAgainst"];
// if (pDist > inputModel.span)
// validationContext.AddFailure("My error message");
// }
//});
RuleFor(x => x.pDist).Must((pointLoad, pDist, validationContext) =>
{
if (!validationContext.RootContextData.ContainsKey("inputModelToCompareAgainst"))
{
return true;
}
var inputModel = (inputModel)validationContext.RootContextData["inputModelToCompareAgainst"];
return pDist <= inputModel.span;
})
.WithMessage("My error message");
}
}
If you are relying on the HTTP request pipeline middleware to invoke the validator, then we need more information. It sounds like inputModel is the user input. Is pointLoad input as well? Is it data from a repository? There may be other options (e.g., perform this validation rule in an inputModel validator, which takes a pointLoad repository as constructor; the validation is still server side, but can be done automatically in the middleware).