Skip to content

Commit 90acefd

Browse files
committed
Migrate to FluentValidation 9.3-preview3
1 parent 2961eac commit 90acefd

File tree

126 files changed

+3285
-2482
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

126 files changed

+3285
-2482
lines changed

src/ServiceStack.Mvc/FluentValidation/FluentValidationModelValidator.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ public override IEnumerable<ModelValidationResult> Validate(object container) {
2626
if (Metadata.Model != null && !_customizations.Skip) {
2727
var selector = _customizations.ToValidatorSelector();
2828
var interceptor = _customizations.GetInterceptor() ?? (_validator as IValidatorInterceptor);
29-
var context = new ValidationContext(Metadata.Model, new PropertyChain(), selector);
29+
IValidationContext context = new ValidationContext<object>(Metadata.Model, new PropertyChain(), selector);
3030
context.RootContextData["InvokedByMvc"] = true;
3131

3232
if(interceptor != null) {

src/ServiceStack.Mvc/FluentValidation/IValidatorInterceptor.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ public interface IValidatorInterceptor {
1414
/// <param name="controllerContext">Controller Context</param>
1515
/// <param name="validationContext">Validation Context</param>
1616
/// <returns>Validation Context</returns>
17-
ValidationContext BeforeMvcValidation(ControllerContext controllerContext, ValidationContext validationContext);
17+
IValidationContext BeforeMvcValidation(ControllerContext controllerContext, IValidationContext validationContext);
1818

1919
/// <summary>
2020
/// Invoked after MVC validation takes place which allows the result to be customized.
@@ -24,7 +24,7 @@ public interface IValidatorInterceptor {
2424
/// <param name="validationContext">Validation Context</param>
2525
/// <param name="result">The result of validation.</param>
2626
/// <returns>Validation Context</returns>
27-
ValidationResult AfterMvcValidation(ControllerContext controllerContext, ValidationContext validationContext, ValidationResult result);
27+
ValidationResult AfterMvcValidation(ControllerContext controllerContext, IValidationContext validationContext, ValidationResult result);
2828
}
2929
}
3030
#endif

src/ServiceStack.Mvc/FluentValidation/PropertyValidatorAdapters/FluentValidationPropertyValidator.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ public override IEnumerable<ModelValidationResult> Validate(object container) {
4242
DisplayName = Rule == null ? null : Rule.DisplayName,
4343
};
4444

45-
var fakeParentContext = new ValidationContext(container);
45+
var fakeParentContext = new ValidationContext<object>(container);
4646
var context = new PropertyValidatorContext(fakeParentContext, fakeRule, Metadata.PropertyName);
4747
var result = Validator.Validate(context);
4848

src/ServiceStack/FluentValidation/AbstractValidator.Partial.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ public void RuleSet(ApplyTo appliesTo, Action action)
113113
}
114114

115115
//TODO: [SYNC] Call from AbstractValidator.Validate/ValidateAsync(context)
116-
private void Init(ValidationContext context)
116+
private void Init(IValidationContext context)
117117
{
118118
if (this.Request == null)
119119
this.Request = context.Request;

src/ServiceStack/FluentValidation/AbstractValidator.cs

Lines changed: 56 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ namespace ServiceStack.FluentValidation {
3333
/// <typeparam name="T">The type of the object being validated</typeparam>
3434
public abstract partial class AbstractValidator<T> : IValidator<T>, IEnumerable<IValidationRule> {
3535
internal TrackingCollection<IValidationRule> Rules { get; } = new TrackingCollection<IValidationRule>();
36-
private Func<CascadeMode> _cascadeMode = () => ValidatorOptions.CascadeMode;
36+
private Func<CascadeMode> _cascadeMode = () => ValidatorOptions.Global.CascadeMode;
3737

3838
/// <summary>
3939
/// Sets the cascade mode for all rules within this validator.
@@ -43,20 +43,12 @@ public CascadeMode CascadeMode {
4343
set => _cascadeMode = () => value;
4444
}
4545

46-
ValidationResult IValidator.Validate(object instance) {
47-
return ((IValidator) this).Validate(new ValidationContext(instance));
48-
}
49-
50-
Task<ValidationResult> IValidator.ValidateAsync(object instance, CancellationToken cancellation) {
51-
return ((IValidator)this).ValidateAsync(new ValidationContext(instance), cancellation);
52-
}
53-
54-
ValidationResult IValidator.Validate(ValidationContext context) {
46+
ValidationResult IValidator.Validate(IValidationContext context) {
5547
context.Guard("Cannot pass null to Validate", nameof(context));
5648
return Validate(ValidationContext<T>.GetFromNonGenericContext(context));
5749
}
5850

59-
Task<ValidationResult> IValidator.ValidateAsync(ValidationContext context, CancellationToken cancellation) {
51+
Task<ValidationResult> IValidator.ValidateAsync(IValidationContext context, CancellationToken cancellation) {
6052
context.Guard("Cannot pass null to Validate", nameof(context));
6153
return ValidateAsync(ValidationContext<T>.GetFromNonGenericContext(context), cancellation);
6254
}
@@ -67,7 +59,9 @@ Task<ValidationResult> IValidator.ValidateAsync(ValidationContext context, Cance
6759
/// <param name="instance">The object to validate</param>
6860
/// <returns>A ValidationResult object containing any validation failures</returns>
6961
public ValidationResult Validate(T instance) {
70-
return Validate(new ValidationContext<T>(instance, new PropertyChain(), ValidatorOptions.ValidatorSelectors.DefaultValidatorSelectorFactory()));
62+
return Validate(new ValidationContext<T>(instance, new PropertyChain(), ValidatorOptions.Global.ValidatorSelectors.DefaultValidatorSelectorFactory()) {
63+
Request = Request
64+
});
7165
}
7266

7367
/// <summary>
@@ -77,7 +71,9 @@ public ValidationResult Validate(T instance) {
7771
/// <param name="cancellation">Cancellation token</param>
7872
/// <returns>A ValidationResult object containing any validation failures</returns>
7973
public Task<ValidationResult> ValidateAsync(T instance, CancellationToken cancellation = new CancellationToken()) {
80-
return ValidateAsync(new ValidationContext<T>(instance, new PropertyChain(), ValidatorOptions.ValidatorSelectors.DefaultValidatorSelectorFactory()), cancellation);
74+
return ValidateAsync(new ValidationContext<T>(instance, new PropertyChain(), ValidatorOptions.Global.ValidatorSelectors.DefaultValidatorSelectorFactory()) {
75+
Request = Request
76+
}, cancellation);
8177
}
8278

8379
/// <summary>
@@ -98,14 +94,28 @@ public virtual ValidationResult Validate(ValidationContext<T> context) {
9894

9995
EnsureInstanceNotNull(context.InstanceToValidate);
10096

101-
var failures = Rules.SelectMany(x => x.Validate(context));
97+
foreach (var rule in Rules) {
98+
var failures = rule.Validate(context);
10299

103-
foreach (var validationFailure in failures.Where(failure => failure != null)) {
104-
result.Errors.Add(validationFailure);
100+
foreach (var validationFailure in failures.Where(failure => failure != null)) {
101+
result.Errors.Add(validationFailure);
102+
}
103+
104+
if (CascadeMode == CascadeMode.Stop && result.Errors.Count > 0) {
105+
// Bail out if we're "failing-fast".
106+
// Check for > 0 rather than == 1 because a rule chain may have overridden the Stop behaviour to Continue
107+
// meaning that although the first rule failed, it actually generated 2 failures if there were 2 validators
108+
// in the chain.
109+
break;
110+
}
105111
}
106112

107113
SetExecutedRulesets(result, context);
108114

115+
if (!result.IsValid && context.ThrowOnFailures) {
116+
RaiseValidationException(context, result);
117+
}
118+
109119
return result;
110120
}
111121

@@ -118,7 +128,6 @@ public virtual ValidationResult Validate(ValidationContext<T> context) {
118128
public async virtual Task<ValidationResult> ValidateAsync(ValidationContext<T> context, CancellationToken cancellation = new CancellationToken()) {
119129
context.Guard("Cannot pass null to Validate", nameof(context));
120130
context.RootContextData["__FV_IsAsyncExecution"] = true;
121-
Init(context);
122131

123132
var result = new ValidationResult();
124133

@@ -137,15 +146,27 @@ public virtual ValidationResult Validate(ValidationContext<T> context) {
137146
foreach (var failure in failures.Where(f => f != null)) {
138147
result.Errors.Add(failure);
139148
}
149+
150+
if (CascadeMode == CascadeMode.Stop && result.Errors.Count > 0) {
151+
// Bail out if we're "failing-fast".
152+
// Check for > 0 rather than == 1 because a rule chain may have overridden the Stop behaviour to Continue
153+
// meaning that although the first rule failed, it actually generated 2 failures if there were 2 validators
154+
// in the chain.
155+
break;
156+
}
140157
}
141158

142159
SetExecutedRulesets(result, context);
143160

161+
if (!result.IsValid && context.ThrowOnFailures) {
162+
RaiseValidationException(context, result);
163+
}
164+
144165
return result;
145166
}
146167

147168
private void SetExecutedRulesets(ValidationResult result, ValidationContext<T> context) {
148-
var executed = context.RootContextData.GetOrAdd("_FV_RuleSetsExecuted", () => new HashSet<string>{"default"});
169+
var executed = context.RootContextData.GetOrAdd("_FV_RuleSetsExecuted", () => new HashSet<string>{RulesetValidatorSelector.DefaultRuleSetName});
149170
result.RuleSetsExecuted = executed.ToArray();
150171
}
151172

@@ -191,14 +212,14 @@ public IRuleBuilderInitial<T, TProperty> RuleFor<TProperty>(Expression<Func<T, T
191212
/// <summary>
192213
/// Invokes a rule for each item in the collection
193214
/// </summary>
194-
/// <typeparam name="TProperty">Type of property</typeparam>
215+
/// <typeparam name="TElement">Type of property</typeparam>
195216
/// <param name="expression">Expression representing the collection to validate</param>
196217
/// <returns>An IRuleBuilder instance on which validators can be defined</returns>
197-
public IRuleBuilderInitialCollection<T, TProperty> RuleForEach<TProperty>(Expression<Func<T, IEnumerable<TProperty>>> expression) {
218+
public IRuleBuilderInitialCollection<T, TElement> RuleForEach<TElement>(Expression<Func<T, IEnumerable<TElement>>> expression) {
198219
expression.Guard("Cannot pass null to RuleForEach", nameof(expression));
199-
var rule = CollectionPropertyRule<TProperty>.Create(expression, () => CascadeMode);
220+
var rule = CollectionPropertyRule<T, TElement>.Create(expression, () => CascadeMode);
200221
AddRule(rule);
201-
var ruleBuilder = new RuleBuilder<T, TProperty>(rule, this);
222+
var ruleBuilder = new RuleBuilder<T, TElement>(rule, this);
202223
return ruleBuilder;
203224
}
204225

@@ -301,7 +322,7 @@ public IConditionBuilder UnlessAsync(Func<T, ValidationContext<T>, CancellationT
301322
/// </summary>
302323
public void Include(IValidator<T> rulesToInclude) {
303324
rulesToInclude.Guard("Cannot pass null to Include", nameof(rulesToInclude));
304-
var rule = IncludeRule.Create<T>(rulesToInclude, () => CascadeMode);
325+
var rule = IncludeRule<T>.Create(rulesToInclude, () => CascadeMode);
305326
AddRule(rule);
306327
}
307328

@@ -310,7 +331,7 @@ public void Include(IValidator<T> rulesToInclude) {
310331
/// </summary>
311332
public void Include<TValidator>(Func<T, TValidator> rulesToInclude) where TValidator : IValidator<T> {
312333
rulesToInclude.Guard("Cannot pass null to Include", nameof(rulesToInclude));
313-
var rule = IncludeRule.Create(rulesToInclude, () => CascadeMode);
334+
var rule = IncludeRule<T>.Create(rulesToInclude, () => CascadeMode);
314335
AddRule(rule);
315336
}
316337

@@ -347,5 +368,16 @@ protected virtual void EnsureInstanceNotNull(object instanceToValidate) {
347368
protected virtual bool PreValidate(ValidationContext<T> context, ValidationResult result) {
348369
return true;
349370
}
371+
372+
/// <summary>
373+
/// Throws a ValidationException. This method will only be called if the validator has been configured
374+
/// to throw exceptions if validation fails. The default behaviour is not to throw an exception.
375+
/// </summary>
376+
/// <param name="context"></param>
377+
/// <param name="result"></param>
378+
/// <exception cref="ValidationException"></exception>
379+
protected virtual void RaiseValidationException(ValidationContext<T> context, ValidationResult result) {
380+
throw new ValidationException(result.Errors);
381+
}
350382
}
351383
}

src/ServiceStack/FluentValidation/AssemblyScanner.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -58,14 +58,14 @@ public static AssemblyScanner FindValidatorsInAssemblies(IEnumerable<Assembly> a
5858
/// Finds all the validators in the assembly containing the specified type.
5959
/// </summary>
6060
public static AssemblyScanner FindValidatorsInAssemblyContaining<T>() {
61-
return FindValidatorsInAssembly(typeof(T).GetTypeInfo().Assembly);
61+
return FindValidatorsInAssembly(typeof(T).Assembly);
6262
}
6363

6464
/// <summary>
6565
/// Finds all the validators in the assembly containing the specified type.
6666
/// </summary>
6767
public static AssemblyScanner FindValidatorsInAssemblyContaining(Type type) {
68-
return FindValidatorsInAssembly(type.GetTypeInfo().Assembly);
68+
return FindValidatorsInAssembly(type.Assembly);
6969
}
7070

7171
private IEnumerable<AssemblyScanResult> Execute() {
@@ -74,7 +74,7 @@ private IEnumerable<AssemblyScanResult> Execute() {
7474
var query = from type in _types
7575
where !type.IsAbstract && !type.IsGenericTypeDefinition
7676
let interfaces = type.GetInterfaces()
77-
let genericInterfaces = interfaces.Where(i => i.GetTypeInfo().IsGenericType && i.GetGenericTypeDefinition() == openGenericType)
77+
let genericInterfaces = interfaces.Where(i => i.IsGenericType && i.GetGenericTypeDefinition() == openGenericType)
7878
let matchingInterface = genericInterfaces.FirstOrDefault()
7979
where matchingInterface != null
8080
select new AssemblyScanResult(matchingInterface, type);

0 commit comments

Comments
 (0)