Reusing validation rules
See original GitHub issueHey @JeremySkinner
You’ve said repeatedly that FV is just for UI validation, but I got it working for domain validation as well.
However I need to duplicate rules again and again - e.g. if I want to validate a Product
entity’s Name
, then I need a validator and a validation rule for the entity itself, and for the create action, and the edit action, etc. This is not a unique problem, every large system has it.
I want to define a shared set of validation rules for a Product
, and reuse them in various places. For example, to validate the create action’s CreateProductViewModel
:
public class CreateProductViewModelValidator : AbstractValidator<CreateProductViewModel>
{
public CreateProductValidator()
{
var productRules = new ProductRules<CreateProductViewModel>();
productRules.Name(v => v.Name);
productRules.Code(v => v.Code);
// ...more rules...
}
}
So instead of redefining rules, I just pick and choose from the shared rules.
Implementation of the shared rules class, which I copied from AbstractValidator.RuleFor()
:
public class ProductRules<T>
{
// validation rule for "Name"
public IRuleBuilderOptions<T, string> ruleForName(Expression<Func<T, string>> expression)
{
// first create the rule builder
var rule = PropertyRule.Create(expression, () => CascadeMode.Continue);
var ruleBuilder = new RuleBuilder<T, string>(rule);
// now define the rule
ruleBuilder.NotNull().NotEmpty().Length(1, 20);
return ruleBuilder;
}
// validation rule for "Code"
public IRuleBuilderOptions<T, int> ruleForCode(Expression<Func<T, int>> expression)
{
var rule = PropertyRule.Create(expression, () => CascadeMode.Continue);
var ruleBuilder = new RuleBuilder<T, int>(rule);
ruleBuilder.GreaterThan(0).LessThan(2000);
return ruleBuilder;
}
// ...more rules...
}
So I can validate a Product
in various places, e.g. Product
, CreateProductViewModel
, EditProductViewModel
, CreateDiscountedProductViewModel
, DeleteObsoleteProductCommand
, etc. etc. Although the “shape” of each entity/viewmodel/DTO will differ, their properties are almost identical - so each has its own validator, but I can pick and choose which rules to run without repeating myself.
Have I broken FV somehow?
- The validator just includes a rule defined in another class instead of defining it itself, but I think the expression caching etc. will still work as normal?
- I used
PropertyRule
andRuleBuilder
which you’ve defined in the “internal”FluentValidation.Internal
namespace. So my code can break whenever FV is updated because I’m (ab)using internal methods. Is there a non-internal way to create those objects?
Issue Analytics
- State:
- Created 6 years ago
- Comments:10 (4 by maintainers)
Top GitHub Comments
You may have already thought about this already and discounted it for some reason, but could you not just do this with extension methods & generics to make it a lot simpler?
…and then use this from inside your CreateProductViewModelValidator like:
…that way you don’t need to mess with PropertyRule or AddRule directly. Also makes it much easier to read and takes advantage of the existing fluent interface.
@grokky1 I’m implementing a validator based off of entity state, Thanks, You just save me a day of troubles.