Reputation: 5606
It's possible in FluentValidation to have a specific error message for a RuleSet?
Something like:
RuleSet("LoginInformation", () =>
{
RuleFor(m => m.Email).NotEmpty().EmailAddress();
RuleFor(m => m.Password).NotEmpty();
}); // I thought I can add a WithMessage here...
That should show a single error message if any of the Rules fail.
Upvotes: 3
Views: 1245
Reputation: 6772
SIMPLE SOLUTION
It is impossible to set error message to RuleSet
directly without changing FluentValidation sources, but you can collect your rules in special list, and set same error message in loop:
RuleSet("LoginInformation", () =>
{
var loginRules = new List<IRuleBuilderOptions<CredentialsModel, object>>
{
RuleFor(m => m.Email).NotEmpty().EmailAddress(),
RuleFor(m => m.Password).NotEmpty()
};
foreach (var rule in rules)
{
// this overload chosen just to show, that all possibilities to set error message still available
rule.WithMessage("An error occured. Additional information: {0}, {1}", (model, value) => value, (model, value) => model.Email);
}
});
There are only 2 cascade mode options, and you can't tell validator to stop all rules on first failure of one, so you need to take only first error from validation result and remove other same errors. For this purpose you can override AbstractValidator
:
public class SingleErrorValidator<T> : AbstractValidator<T>
{
public override ValidationResult Validate(ValidationContext<T> context)
{
var result = base.Validate(context);
if (result.IsValid)
return result;
var singleErrorList = new List<ValidationFailure> { result.Errors.First() };
var singleErrorResult = new ValidationResult(singleErrorList);
return singleErrorResult;
}
}
ADVANCED SOLUTION:
If you forked FluentValidation and can modify the source code in your solution, then you can make reuseable approach for common error message and add next RuleSet
method overload in AbstractValidator
class (you can't use extension method or inheritance because important fields have private modifier):
public void RuleSet(string ruleSetName, Func<List<IRuleBuilderOptions<T, object>>> function, Action<IRuleBuilderOptions<T, object>> writeRuleMessage)
{
ruleSetName.Guard("A name must be specified when calling RuleSet.");
function.Guard("A ruleset definition must be specified when calling RuleSet.");
using (nestedValidators.OnItemAdded(r => r.RuleSet = ruleSetName))
{
var list = function();
foreach (var rule in list)
{
writeRuleMessage(rule);
}
}
}
Then you can use RuleSet
next way:
RuleSet("LoginInformation", () =>
{
var rules = new List<IRuleBuilderOptions<B, object>>()
{
RuleFor(x => x.Name).NotEmpty().EmailAddress(),
RuleFor(x => x.Email).NotEmpty()
};
return rules;
}, (rule) =>
{
rule.WithMessage("An error occured");
}); // still can use another overloads, that allow access to model and validated properties
CONCLUSION
Both solutions are able to work, but I recommend to use 1-st due to KISS principle.
Upvotes: 1