Skip to content

Commit cb526a1

Browse files
authored
Merge pull request #51 from kolan72/main
Update from main.
2 parents c1f02d2 + d445f8d commit cb526a1

16 files changed

Lines changed: 493 additions & 41 deletions

File tree

CHANGELOG.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,14 @@
1+
## 0.9.0
2+
3+
- Added quick validation support via `QuickValidator` and its `Validate<T>` overloads.
4+
- Improve performance by applying options in `ExpressValidator<TObj, TOptions>` during the `ExpressValidatorBuilder<TObj, TOptions>.Build` call instead of at validation time.
5+
- Introduce the `Unit` readonly struct.
6+
- Add 'Nuances Of Using The Library' README Chapter.
7+
- Add 'Nuances Of Using The Library' NuGet README Chapter.
8+
- Add 'Quick Validation' README Chapter.
9+
- Add 'Quick Validation' NuGet README Chapter.
10+
11+
112
## 0.5.0
213

314
- Introduced the `IExpressValidatorBuilder<TObj, TOptions>.BuildAndValidate<TObj, TOptions>(TObj, TOptions)` extension method.

README.md

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ ExpressValidator is a library that provides the ability to validate objects usin
1818
- Supports adding a property or field for validation.
1919
- Verifies that a property expression is a property and a field expression is a field, and throws `ArgumentException` if it is not.
2020
- Supports adding a `Func` that provides a value for validation.
21+
- Provides quick validation (refers to ease of use).
2122
- Supports asynchronous validation.
2223
- Targets .NET Standard 2.0+
2324

@@ -129,6 +130,49 @@ if(!result2.IsValid)
129130
}
130131
```
131132

133+
## ⏩ Quick Validation
134+
135+
Quick validation is convenient for primitive types or types without properties/fields (here, 'quick' refers to usability, not performance). Simply call `QuickValidator.Validate` on the object with a preconfigured rule:
136+
137+
```csharp
138+
var value = 5;
139+
// result.IsValid == false
140+
// result.Errors[0].PropertyName == "value"
141+
var result = QuickValidator.Validate(
142+
value,
143+
(opt) => opt.GreaterThan(10),
144+
nameof(value));
145+
```
146+
147+
For complex types, use FluentValidation's `ChildRules` method:
148+
149+
```csharp
150+
var obj = new ObjToValidate() { I = -1, PercentValue1 = 101 };
151+
// result.IsValid == false
152+
// result.Errors.Count == 2
153+
// result.Errors[0].PropertyName == "obj.I"; result.Errors[1].PropertyName == "obj.PercentValue1"
154+
var result = QuickValidator.Validate(
155+
obj,
156+
(opt) =>
157+
opt
158+
.ChildRules((v) => v.RuleFor(o => o.I).GreaterThan(0))
159+
.ChildRules((v) => v.RuleFor(o => o.PercentValue1).InclusiveBetween(0, 100)),
160+
nameof(obj));
161+
```
162+
163+
## 🧩 Nuances Of Using The Library
164+
165+
For `ExpressValidatorBuilder` methods (`AddFunc`, `AddProperty`, and `AddField`), the overridden property name (set via `FluentValidation`'s `OverridePropertyName` method in `With(Async)Validation`) takes precedence over the property name passed as a string or via `Expression` in `AddFunc`/`AddProperty`/`AddField`.
166+
For example, for the `ObjToValidate` object from the 'Quick Start' chapter, `result.Errors[0].PropertyName` will equal "percentSum" (the property name overridden in the validation rule):
167+
```csharp
168+
// result.Errors[0].PropertyName == "percentSum"
169+
var result = new ExpressValidatorBuilder<ObjToValidate>()
170+
.AddFunc(o => o.PercentValue1 + o.PercentValue2, "sum")
171+
.WithValidation((o) => o.InclusiveBetween(0, 100)
172+
.OverridePropertyName("percentSum"))
173+
.BuildAndValidate(new ObjToValidate() { PercentValue1 = 200});
174+
```
175+
132176
## ❌ Drawbacks
133177

134178
- Non-canonical way of using of FluentValidation.

src/ExpressValidator.Extensions.DependencyInjection/CHANGELOG.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,12 @@
1+
## 0.3.5
2+
3+
- Reduced unnecessary updates to validator parameters by listening to `IOptionsMonitor.Change` with named validation options.
4+
- Update 'ExpressValidator.Extensions.DependencyInjection' to ExpressValidator 0.5.0.
5+
- Update Microsoft nuget packages.
6+
- Update README with a libraries/dependencies table.
7+
- Add a configuration section in 'ExpressValidator.Extensions.DependencyInjection.Sample' for testing purposes.
8+
9+
110
## 0.3.2
211

312
- Update to ExpressValidator 0.2.0.

src/ExpressValidator.Extensions.DependencyInjection/ExpressValidator.Extensions.DependencyInjection.csproj

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
<PropertyGroup>
44
<TargetFramework>netstandard2.0</TargetFramework>
55
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
6-
<Version>0.3.2</Version>
6+
<Version>0.3.5</Version>
77
<GenerateDocumentationFile>true</GenerateDocumentationFile>
88
<Authors>Andrey Kolesnichenko</Authors>
99
<PackageLicenseExpression>MIT</PackageLicenseExpression>
@@ -15,7 +15,7 @@
1515
<PackageTags>FluentValidation Validation DependencyInjection</PackageTags>
1616
<Description>The ExpressValidator.Extensions.DependencyInjection package extends ExpressValidator to provide integration with Microsoft Dependency Injection.</Description>
1717
<Copyright>Copyright 2024 Andrey Kolesnichenko</Copyright>
18-
<AssemblyVersion>0.3.2.0</AssemblyVersion>
18+
<AssemblyVersion>0.3.5.0</AssemblyVersion>
1919
</PropertyGroup>
2020

2121
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">

src/ExpressValidator/ExpressValidator.TOptions.cs

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -14,15 +14,18 @@ namespace ExpressValidator
1414
/// <typeparam name="TOptions"></typeparam>
1515
public class ExpressValidator<TObj, TOptions> : IExpressValidator<TObj>
1616
{
17-
private readonly TOptions _options;
1817
private readonly IEnumerable<IObjectValidator<TObj, TOptions>> _validators;
1918
private readonly OnFirstPropertyValidatorFailed _validationMode;
2019

2120
internal ExpressValidator(TOptions options, IEnumerable<IObjectValidator<TObj, TOptions>> validators, OnFirstPropertyValidatorFailed validationMode)
2221
{
23-
_options = options;
2422
_validators = validators;
2523
_validationMode = validationMode;
24+
25+
foreach (var validator in _validators)
26+
{
27+
validator.ApplyOptions(options);
28+
}
2629
}
2730

2831
public ValidationResult Validate(TObj obj)
@@ -44,8 +47,6 @@ private async Task<ValidationResult> ValidateWithBreakAsync(TObj obj, Cancellati
4447
foreach (var validator in _validators)
4548
{
4649
token.ThrowIfCancellationRequested();
47-
48-
validator.ApplyOptions(_options);
4950
var (IsValid, Failures) = await validator.ValidateAsync(obj, token).ConfigureAwait(false);
5051
if (!IsValid)
5152
{
@@ -61,8 +62,6 @@ private async Task<ValidationResult> ValidateWithContinueAsync(TObj obj, Cancell
6162
foreach (var validator in _validators)
6263
{
6364
token.ThrowIfCancellationRequested();
64-
65-
validator.ApplyOptions(_options);
6665
var (IsValid, Failures) = await validator.ValidateAsync(obj, token);
6766
if (!IsValid)
6867
{
@@ -76,7 +75,6 @@ private ValidationResult ValidateWithBreak(TObj obj)
7675
{
7776
foreach (var validator in _validators)
7877
{
79-
validator.ApplyOptions(_options);
8078
var (IsValid, Failures) = validator.Validate(obj);
8179
if (!IsValid)
8280
{
@@ -91,7 +89,6 @@ private ValidationResult ValidateWithContinue(TObj obj)
9189
var currentFailures = new List<ValidationFailure>();
9290
foreach (var validator in _validators)
9391
{
94-
validator.ApplyOptions(_options);
9592
var (IsValid, Failures) = validator.Validate(obj);
9693
if (!IsValid)
9794
{

src/ExpressValidator/ExpressValidator.csproj

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
<PropertyGroup>
44
<TargetFramework>netstandard2.0</TargetFramework>
55
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
6-
<Version>0.5.0</Version>
6+
<Version>0.9.0</Version>
77
<GenerateDocumentationFile>true</GenerateDocumentationFile>
88
<Authors>Andrey Kolesnichenko</Authors>
99
<Description>ExpressValidator is a library that provides the ability to validate objects using the FluentValidation library, but without object inheritance from `AbstractValidator`.</Description>
@@ -15,7 +15,7 @@
1515
<PackageIcon>ExpressValidator.png</PackageIcon>
1616
<PackageReadmeFile>NuGet.md</PackageReadmeFile>
1717
<PackageIconUrl />
18-
<AssemblyVersion>0.5.0.0</AssemblyVersion>
18+
<AssemblyVersion>0.9.0.0</AssemblyVersion>
1919
<FileVersion>0.0.0.0</FileVersion>
2020
</PropertyGroup>
2121

src/ExpressValidator/PropertyValidators/ExpressPropertyValidator.TOptions.cs

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ internal class ExpressPropertyValidator<TObj, TOptions, T> : IExpressPropertyVal
1111
{
1212
private readonly string _propName;
1313
private readonly Func<TObj, T> _propertyFunc;
14+
private TypeValidatorBase<T> _typeValidator;
1415

1516
private Action<TOptions, IRuleBuilderOptions<T, T>> _actionWithOptions;
1617

@@ -30,9 +31,7 @@ public void SetValidation(Action<TOptions, IRuleBuilderOptions<T, T>> action)
3031

3132
public Task<(bool IsValid, List<ValidationFailure> Failures)> ValidateAsync(TObj obj, CancellationToken token = default)
3233
{
33-
var typeValidator = new TypeAsyncValidator<T>();
34-
typeValidator.SetValidation(_action, _propName);
35-
return typeValidator.ValidateExAsync(_propertyFunc(obj), token);
34+
return _typeValidator.ValidateExAsync(_propertyFunc(obj), token);
3635
}
3736

3837
public (bool IsValid, List<ValidationFailure> Failures) Validate(TObj obj)
@@ -41,14 +40,26 @@ public void SetValidation(Action<TOptions, IRuleBuilderOptions<T, T>> action)
4140
{
4241
throw new InvalidOperationException();
4342
}
44-
var typeValidator = new TypeValidator<T>();
45-
typeValidator.SetValidation(_action, _propName);
46-
return typeValidator.ValidateEx(_propertyFunc(obj));
43+
return _typeValidator.ValidateEx(_propertyFunc(obj));
4744
}
4845

4946
public void ApplyOptions(TOptions options)
5047
{
5148
_action =_actionWithOptions.Apply(options);
49+
SetTypeValidator();
50+
}
51+
52+
private void SetTypeValidator()
53+
{
54+
if (IsAsync)
55+
{
56+
_typeValidator = new TypeAsyncValidator<T>();
57+
}
58+
else
59+
{
60+
_typeValidator = new TypeValidator<T>();
61+
}
62+
_typeValidator.SetValidation(_action, _propName);
5263
}
5364

5465
public bool IsAsync { get; }
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
namespace ExpressValidator.QuickValidation
2+
{
3+
/// <summary>
4+
/// Determines how the property name is set when quick validation fails.
5+
/// </summary>
6+
public enum PropertyNameMode
7+
{
8+
/// <summary>
9+
/// Use the literal string "Input" as the property name.
10+
/// </summary>
11+
Default,
12+
13+
/// <summary>
14+
/// Use the type's name (typeof(T).Name) of the validated object as the property name.
15+
/// </summary>
16+
TypeName
17+
}
18+
}
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
using ExpressValidator.Extensions;
2+
using FluentValidation;
3+
using FluentValidation.Results;
4+
using System;
5+
6+
namespace ExpressValidator.QuickValidation
7+
{
8+
/// <summary>
9+
/// Provides methods for quick validation.
10+
/// </summary>
11+
public static class QuickValidator
12+
{
13+
private const string FALLBACK_PROP_NAME = "Input";
14+
15+
/// <summary>
16+
/// Validates the given object instance using <paramref name="action"/>.
17+
/// </summary>
18+
/// <typeparam name="T">The type of the object to validate.</typeparam>
19+
/// <param name="obj">The object to validate.</param>
20+
/// <param name="action">Action to add validators.</param>
21+
/// <param name="propName">The name of the property if the validation fails.
22+
/// If <see langword="null"/>, "Input" will be used.</param>
23+
/// <param name="onSuccessValidation">Specifies a method to execute when validation succeeds.</param>
24+
/// <returns></returns>
25+
public static ValidationResult Validate<T>(T obj, Action<IRuleBuilderOptions<T, T>> action, string propName, Action<T> onSuccessValidation = null)
26+
{
27+
return ValidateInner<T>(obj, action, propName ?? FALLBACK_PROP_NAME, onSuccessValidation);
28+
}
29+
30+
/// <summary>
31+
/// Validates the given object instance using <paramref name="action"/>.
32+
/// </summary>
33+
/// <typeparam name="T">The type of the object to validate.</typeparam>
34+
/// <param name="obj">The object to validate.</param>
35+
/// <param name="action">Action to add validators.</param>
36+
/// <param name="mode"><see cref="PropertyNameMode"/>.
37+
/// If <see cref="PropertyNameMode.Default"/>, "Input" will be used.</param>
38+
/// <param name="onSuccessValidation">Specifies a method to execute when validation succeeds.</param>
39+
/// <returns></returns>
40+
public static ValidationResult Validate<T>(T obj, Action<IRuleBuilderOptions<T, T>> action, PropertyNameMode mode = PropertyNameMode.Default, Action<T> onSuccessValidation = null)
41+
{
42+
return ValidateInner<T>(obj, action, GetPropName<T>(mode), onSuccessValidation);
43+
}
44+
45+
private static ValidationResult ValidateInner<T>(T obj, Action<IRuleBuilderOptions<T, T>> action, string propName, Action<T> onSuccessValidation = null)
46+
{
47+
var eb = new ExpressValidatorBuilder<Unit>();
48+
return eb.AddFunc((_) => obj,
49+
propName,
50+
onSuccessValidation)
51+
.WithValidation(action)
52+
.BuildAndValidate(Unit.Default);
53+
}
54+
55+
private static string GetPropName<T>(PropertyNameMode mode) => mode == PropertyNameMode.Default ? FALLBACK_PROP_NAME : typeof(T).Name;
56+
}
57+
}

src/ExpressValidator/Unit.cs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
namespace ExpressValidator
2+
{
3+
public readonly struct Unit
4+
{
5+
public static readonly Unit Default = new Unit();
6+
}
7+
}

0 commit comments

Comments
 (0)