CascadeMode StopOnFirstFailure doesn't work - fluentvalidation

public class Validator : AbstractValidator<Query>
{
public Validator()
{
CascadeMode = CascadeMode.StopOnFirstFailure;
RuleFor(x => x.A).NotEmpty();
RuleFor(x => x.B).NotEmpty();
RuleFor(x => x).MustAsync(...);
}
}
I'like to construct validator which dones't call MustAsync when above rules are not met. Unfortunately settings CascadeMode to StopOnFirstFailure in validator doesn't do the work.

As stated by the author
That's the correct behaviour - CascadeMode only affects validators
within the same rule chain. Independent calls to RuleFor are separate,
and not dependent on the success or failure of other rules.
See this.
So it would apply for this case
Rulefor(x => x.A)
.NotEmpty()
.Length(10);
=> the Length validation would only be applied if A is not empty.
So you'll have to use a When extension in your MustAsync rule, checking if A and B are not empty (or an if around this rule).

Related

Validate multiple properties with one message

I'm trying to validate a class that has three required properties.
If one or more of them is null it should trigger a single validation message.
Is there a idiomatic way to describe this in fluent validator?
I'm looking at dependant rules but the bottom of the documentation's page advises against using them.
Furthermore I still want to validate all three properties. I just don't want duplicate messages.
I noticed RuleSets, but these seem to serve a different purpose.
Alternatively I could create a validator specifically for these three options but without message and then chain the new validator in the original one. Then I think I can give that one a single message.
But that's a lot of ceremony for a system that is built around being readable.
So looking for a readable way to express validation for three fields with a single message as result.
There are 3 main ways you can do this with FluentValidation: Conditions, dependent rules or a custom rule.
Conditions
You can use 3 separate rule declarations with When conditions to ensure that you only ever get a single validation message.
RuleFor(x => x.Property1).NotNull()
.WithMessage("At least one is required");
RuleFor(x => x.Property2).NotNull()
.When(x => x.Property2 != null)
.WithMessage("At least one is required");
RuleFor(x => x.Property3).NotNull()
.When(x => x.Property1 != null && x.Property2 != null)
.WithMessage("At least one is required");
Dependent Rules
RuleFor(x => x.Property1).NotNull()
.WithMessage("At least one is required")
.DependentRules(() => {
RuleFor(x => x.Property2).NotNull()
.WithMessage("At least one is required")
.DependentRules(() => {
RuleFor(x => x.Property3).NotNull().WithMessage("At least one is required");
});
});
I don't particularly like this approach - I think it's hard to read (hence the warning in the documentation), but if you like this approach it'll work just fine.
Custom logic
RuleFor(x => x)
.Must(x => x.Property1 != null && x.Property2 != null && x.Property3 != null)
.WithMessage("At least one is required");
This approach is slightly different as it creates a model-level rule, so the error message will be associated with the entire model, rather than with a specific property.
Stop the validator when the first rule fails by setting the CascadeMode property:
public class MyClassValidator : AbstractValidator<MyClass>
{
public DestinationDeviceValidator()
{
this.CascadeMode = CascadeMode.Stop;
this.RuleFor(x => x.Property1)
.NotNull();
this.RuleFor(x => x.Property2)
.NotNull();
this.RuleFor(x => x.Property3)
.NotNull();
}
}

Fluent Validator for when another property is true

I am trying to use FluentValidation to validate the property 'Username' if another property 'Found' is true.
Object Contains:
string Username
bool Found
RuleFor(x => x.Username)
.NotEmpty().DependentRules(() => {
RuleFor(y => y.Found).Equals(true); //Not valid syntax
})
.WithMessage("Not Found");
Unfortunately, there seems to be no easy way to do this?
Use the When clause.
RuleFor(x => x.Username).NotEmpty().When(x => x.Found);
Working example
The dependent rules is a bit different; basically the rules specified in the dependent rules block will only be tested if the rule they're attached to passes.
As per the doco
RuleFor(x => x.Surname).NotNull().DependentRules(() => {
RuleFor(x => x.Forename).NotNull();
});
Here the rule against Forename will only be run if the Surname rule passes.

Exclude in DecorateAllWith, using structuremap

I used following code to decorate all my ICommandHandler<> with Decorator1<>, using structuremap:
ObjectFactory.Configure(x =>
{
....
x.For(typeof(ICommandHandler<>))
.DecorateAllWith(typeof(Decorator1<>),
instance => instance.ReturnedType.Assembly == typeof(Class1).Assembly);
}
Is there any way to exclude one of my concrete CommandHandlers from this decoration?

Using FluentValidation's WithMessage method with parameters with a WarningMessage

I am new at FluentValidation in general. I am writing a validator, and I can't seem to figure out how to do a .WithMessage with a WarningMessage instead of an ErrorMessage and use params.
I can do this:
RuleFor(x => x.Endorsement)
.Must((coverage, endorsement) => HaveCoveragePerAcreOverMinimum(_coverage, coverage))
.When(x => (!HaveSpecialRequest(_coverage) && !HavePermissionsToOverrideLimits()))
.WithMessage("Some error message {0}", x => x.MyError);
But that sets it as an ErrorMessage and I need a Warning Message. I tried this but no dice:
RuleFor(x => x.Endorsement)
.Must((coverage, endorsement) => HaveCoveragePerAcreOverMinimum(_coverage, coverage))
.When(x => (!HaveSpecialRequest(_coverage) && !HavePermissionsToOverrideLimits()))
.WithMessage(new WarningMessage("Some warning message {0}", x => x.MyError));
There's no direct implementation of Warning message in FluentValidation (or Mvc's ModelState).
In FluentValidation, you've got a
WithState() extension method that you can use for this purpose.
First, you can create an enum
public enum ValidationErrorLevel
{
Error,
Warning
}
Then, you can write a few extension methods, in a static class, to use warnings and errors.
One to use in your Validator classes
public static IRuleBuilderOptions<T, TProperty> AsWarning<T, TProperty>(this IRuleBuilderOptions<T, TProperty> rule)
{
return rule.WithState<T, TProperty>(x => ValidationErrorLevel.Warning);
}
You can use it this way
RuleFor(x => x.Endorsement)
.Must((coverage, endorsement) => HaveCoveragePerAcreOverMinimum(_coverage, coverage))
.When(x => (!HaveSpecialRequest(_coverage) && !HavePermissionsToOverrideLimits()))
.WithMessage("Some error message {0}", x => x.MyError)
.AsWarning();
Few other to use to manage your validation results
public static IList<ValidationFailure> GetWarnings(this ValidationResult result)
{
return result.Errors.Where(m => m.CustomState != null && Convert.ToInt32(m.CustomState) == (int)ValidationErrorLevel.Warning).ToList();
}
public static IList<ValidationFailure> GetErrors(this ValidationResult result)
{
return result.Errors.Except(result.GetWarnings()).ToList();
}
Then, you should use
validator.Validate(<someclass>)
instead of
validator.ValidateAndThrow(<someclass>)
var results = validator.Validate(<someclass>);
You can then put errors in ModelState, for example
foreach (var error in result.GetErrors()) {
ModelState.AddModelError(error.PropertyName, error.ErrorMessage);
}
and do something else for Warnings, for example put it in TempData
TempData["warnings"] = new List<string>();
((List<string>)TempData[WarningMessageKey]).AddRange(result.GetWarnings().Select(warning => warning.ErrorMessage));
Then you can display them like any other TempData.
Validation (in general) attaches errors to the ModelState - which in itself is an object so if you put a break point on the if(ModelState.IsValid) line you can look at the other properties of the ModelState.
The thing with errors (and error messages) they are either there or not. If there is no issue there is no error message and if there is an issue an error message will be added.
If I were you, if the model state is not valid in your controller I would get all error messages using
var allErrors = ModelState.Values.SelectMany(v => v.Errors);
and then iterate over each one and decide what you want to do with it.
I hope this answers or at least helps as I am not totally sure what you are asking / trying to get to.

Rhino Mocks: Repeat.Once() not working?

Can anyone tell me why in the world the following test is not failing?
[Test]
public void uhh_what() {
var a = MockRepository.GenerateMock<IPrebuiltNotifier>();
a.Expect(x => x.Notify()).Repeat.Once();
a.Notify();
a.Notify();
a.VerifyAllExpectations();
}
Really need a second pair of eyes to confirm I'm not crazy...now I'm worried that all my tests are unreliable.
There is already a thread on the RhinoMocks group.
GenerateMock creates a dynamic mock. The dynamic mock allows calls that are not specified (=expected). If this happens, it just returns null (or the default value of the return type).
Note: Repeat is a specification of the behaviour (like Stub), not the expectation even if specified in an expectation.
If you want to avoid having more then a certain number of calls, you could write:
[Test]
public void uhh_what()
{
var a = MockRepository.GenerateMock<IPrebuiltNotifier>();
a.Expect(x => x.Notify()).Repeat.Once();
a.Stub(x => x.Notify()).Throw(new InvalidOperationException("gotcha"));
a.Notify();
// this fails
a.Notify();
a.VerifyAllExpectations();
}
Or
[Test]
public void uhh_what()
{
var a = MockRepository.GenerateMock<IPrebuiltNotifier>();
a.Notify();
a.Notify();
// this fails
a.AssertWasCalled(
x => x.Notify(),
o => o.Repeat.Once());
}
When using GenerateMock (or with Dynamic Mocks in general) I always mentally insert the following:
a.Expect(x => x.Notify()).Repeat.*[AtLeast]*Once();