Skip to content

Commit 88e434c

Browse files
authored
Merge pull request #66 from kolan72/main
Update dev-di from main.
2 parents 26652d0 + 738ad9e commit 88e434c

File tree

12 files changed

+198
-25
lines changed

12 files changed

+198
-25
lines changed

CHANGELOG.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,17 @@
1+
## 0.12.2
2+
3+
- Move the instance field `TypeValidatorBase._shouldBeComparedToNull` to a static readonly field (renamed to `_canBeNull`) to cache the reflection result per `TypeValidatorBase<T>` type and eliminate redundant per-instance evaluations.
4+
- Remove the eagerly allocated `NotNullValidationMessageProvider` during `ExpressValidatorBuilder` configuration, and instantiate `NotNullValidationMessageProvider<object, T>` only when the value is null.
5+
Deprecate `NotNullValidationMessageProvider.GetMessage(ValidationContext<T>)`.
6+
- Update to FluentValidation 12.1.0.
7+
- Rename private `TypeValidatorBase.HasOnlyNullOrEmptyValidators` to `HasNonEmptyValidators` (inverting the boolean logic) and remove the negation of this property in the `ShouldValidate` method.
8+
- DRY refactor of null validation in `TypeValidatorBase<T>`.
9+
- Add tests for null-tolerance validation in `QuickValidator`.
10+
- Add test for validating a primitive type using `ExpressValidatorBuilder`.
11+
- Add a unit test that verifies `ExpressValidator` does not throw when members are null and no null-related validators are used.
12+
- Edit README.md and NuGet.md.
13+
14+
115
## 0.12.0
216

317
- Support .NET 8.0 and FluentValidation 12.0.0.

README.md

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,15 @@ 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 and easy validation using the `QuickValidator`.
21+
- Provides quick and easy validation using `QuickValidator`, with built-in tolerance for `null` values.
2222
- Supports asynchronous validation.
2323
- Targets .NET Standard 2.0+
2424

2525
## 📜 Documentation
2626

27-
For details, please check the [API documentation](https://www.tmfexplorer.com/ExpressValidator/api/ExpressValidator.html).
27+
> See the [API documentation](https://www.tmfexplorer.com/ExpressValidator/api/ExpressValidator.html) for reference.
28+
>
29+
> Learn more on [DeepWiki](https://deepwiki.com/kolan72/ExpressValidator/2-core-library-%28expressvalidator%29).
2830
2931

3032
## 🚀 Quick Start
@@ -181,7 +183,8 @@ var result = QuickValidator.Validate(
181183
.ChildRules((v) => v.RuleFor(o => o.PercentValue1).InclusiveBetween(0, 100)),
182184
nameof(obj));
183185
```
184-
The `QuickValidator` also provides a `ValidateAsync` method for asynchronous validation.
186+
The `QuickValidator` also provides a `ValidateAsync` method for asynchronous validation.
187+
It is also tolerant of `null` values, i.e., it avoids exceptions when the input is null.
185188

186189
## 🧩 Nuances Of Using The Library
187190

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.12
2+
3+
- Support .NET 8.0 and FluentValidation 12.0.0.
4+
- Update Microsoft nuget packages.
5+
- Update ExpressValidator NuGet package to v0.12.0.
6+
- Update NUnit NuGet package to v4.4.0.
7+
- Retarget ExpressValidator.Extensions.DependencyInjection.Sample to .NET 8.0.
8+
9+
110
## 0.3.9
211

312
- Update ExpressValidator nuget package.

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
<TargetFrameworks>netstandard2.0;net8.0</TargetFrameworks>
55
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
6-
<Version>0.3.9</Version>
6+
<Version>0.3.12</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.9.0</AssemblyVersion>
18+
<AssemblyVersion>0.3.12.0</AssemblyVersion>
1919
</PropertyGroup>
2020

2121
<ItemGroup Condition="'$(TargetFramework)' == 'netstandard2.0'">

src/ExpressValidator.Extensions.DependencyInjection/README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@
66
- Additionally, the `IExpressValidatorBuilder<ObjToValidate, ValidationParametersOptions>` interface can be configured and registered to update the validator parameters when the `ValidationParametersOptions` change.
77
- Ability to dynamically update the validator parameters from options bound to the configuration section without restarting the application by configuring the `IExpressValidatorWithReload<ObjToValidate>` interface.
88

9+
## 📜 Documentation
10+
11+
Explore the API documentation and in-depth details on [DeepWiki](https://deepwiki.com/kolan72/ExpressValidator/3-dependency-injection-extension).
12+
913
## 🚀 Usage
1014

1115
```csharp

src/ExpressValidator/ExpressValidator.csproj

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
<PropertyGroup>
44
<TargetFrameworks>netstandard2.0;net8.0</TargetFrameworks>
55
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
6-
<Version>0.12.0</Version>
6+
<Version>0.12.2</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.12.0.0</AssemblyVersion>
18+
<AssemblyVersion>0.12.2.0</AssemblyVersion>
1919
<FileVersion>0.0.0.0</FileVersion>
2020
</PropertyGroup>
2121

@@ -24,7 +24,7 @@
2424
</ItemGroup>
2525

2626
<ItemGroup Condition="'$(TargetFramework)' == 'net8.0'">
27-
<PackageReference Include="FluentValidation" Version="12.0.0" />
27+
<PackageReference Include="FluentValidation" Version="12.1.0" />
2828
</ItemGroup>
2929

3030
<ItemGroup>

src/ExpressValidator/TypeValidators/NotNullValidationMessageProvider.cs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using FluentValidation;
22
using FluentValidation.Validators;
3+
using System;
34

45
namespace ExpressValidator
56
{
@@ -11,9 +12,23 @@ public NotNullValidationMessageProvider(string propName)
1112
_propName = propName;
1213
}
1314

15+
public string GetMessage() => GetDefaultMessageTemplate(null);
16+
17+
#pragma warning disable S1133 // Deprecated code should be removed
18+
[Obsolete("This method is obsolete")]
19+
#pragma warning restore S1133 // Deprecated code should be removed
1420
public string GetMessage(ValidationContext<T> context)
1521
{
1622
return context.MessageFormatter.AppendPropertyName(_propName).BuildMessage(GetDefaultMessageTemplate(null));
1723
}
1824
}
25+
26+
internal static class NullFallbackMessageProvider
27+
{
28+
public static string GetMessage<T>(string propName, ValidationContext<T> context)
29+
{
30+
var validator = new NotNullValidationMessageProvider<T>(propName);
31+
return context.MessageFormatter.AppendPropertyName(propName).BuildMessage(validator.GetMessage());
32+
}
33+
}
1934
}

src/ExpressValidator/TypeValidators/TypeValidatorBase.cs

Lines changed: 11 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -11,17 +11,11 @@ namespace ExpressValidator
1111
internal abstract class TypeValidatorBase<T> : AbstractValidator<T>
1212
{
1313
protected IRuleBuilderOptions<T, T> _ruleBuilderInitial;
14-
private NotNullValidationMessageProvider<T> _nullMessageProvider;
1514

1615
private IValidationRule<T> _rule;
1716
private string _propName;
1817

19-
private readonly bool _shouldBeComparedToNull;
20-
21-
protected TypeValidatorBase()
22-
{
23-
_shouldBeComparedToNull = !typeof(T).IsValueType || (Nullable.GetUnderlyingType(typeof(T)) != null);
24-
}
18+
private static readonly bool _canBeNull = !typeof(T).IsValueType || (Nullable.GetUnderlyingType(typeof(T)) != null);
2519

2620
protected override void OnRuleAdded(IValidationRule<T> rule)
2721
{
@@ -36,9 +30,9 @@ protected override void OnRuleAdded(IValidationRule<T> rule)
3630
/// <returns></returns>
3731
protected override bool PreValidate(ValidationContext<T> context, ValidationResult result)
3832
{
39-
if (_shouldBeComparedToNull && EqualityComparer<T>.Default.Equals(context.InstanceToValidate, default))
33+
if (IsValueNull(context.InstanceToValidate))
4034
{
41-
result.Errors.Add(new ValidationFailure(_propName, _nullMessageProvider.GetMessage(context)));
35+
result.Errors.Add(new ValidationFailure(_propName, NullFallbackMessageProvider.GetMessage(_propName, context)));
4236
return false;
4337
}
4438
return true;
@@ -55,9 +49,7 @@ public void SetValidation(Action<IRuleBuilderOptions<T, T>> action, string propN
5549
_ruleBuilderInitial = _ruleBuilderInitial.OverridePropertyName(_propName);
5650
}
5751

58-
_nullMessageProvider = new NotNullValidationMessageProvider<T>(_propName);
59-
60-
HasOnlyNullOrEmptyValidators = AllValidatorsAreNullOrEmpty();
52+
HasNonEmptyValidators = !AllValidatorsAreNullOrEmpty();
6153
}
6254

6355
public async Task<(bool IsValid, List<ValidationFailure> Failures)> ValidateExAsync(T value, CancellationToken token = default)
@@ -94,9 +86,14 @@ public void SetValidation(Action<IRuleBuilderOptions<T, T>> action, string propN
9486

9587
internal abstract bool? IsAsync { get; }
9688

97-
protected bool ShouldValidate(T value) =>!_shouldBeComparedToNull || !EqualityComparer<T>.Default.Equals(value, default) || !HasOnlyNullOrEmptyValidators;
89+
protected bool ShouldValidate(T value) => !IsValueNull(value) || HasNonEmptyValidators;
90+
91+
private static bool IsValueNull(T value)
92+
{
93+
return _canBeNull && EqualityComparer<T>.Default.Equals(value, default);
94+
}
9895

99-
private bool HasOnlyNullOrEmptyValidators { get; set; }
96+
private bool HasNonEmptyValidators { get; set; }
10097

10198
private bool AllValidatorsAreNullOrEmpty()
10299
{

src/ExpressValidator/docs/NuGet.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ ExpressValidator is a library that provides the ability to validate objects usin
88
- Supports adding a property or field for validation.
99
- Verifies that a property expression is a property and a field expression is a field, and throws `ArgumentException` if it is not.
1010
- Supports adding a `Func` that provides a value for validation.
11-
- Provides quick and easy validation using the `QuickValidator`.
11+
- Provides quick and easy validation using `QuickValidator`, with built-in tolerance for `null` values.
1212
- Supports asynchronous validation.
1313
- Targets .NET Standard 2.0+
1414

@@ -144,7 +144,8 @@ var result = QuickValidator.Validate(
144144
.ChildRules((v) => v.RuleFor(o => o.PercentValue1).InclusiveBetween(0, 100)),
145145
nameof(obj));
146146
```
147-
The `QuickValidator` also provides a `ValidateAsync` method for asynchronous validation.
147+
The `QuickValidator` also provides a `ValidateAsync` method for asynchronous validation.
148+
It is also tolerant of `null` values, i.e., it avoids exceptions when the input is null.
148149

149150
## Nuances Of Using The Library
150151

tests/ExpressValidator.Tests/ExpressValidatorTests.cs

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
using FluentValidation;
1+
using ExpressValidator.Extensions;
2+
using FluentValidation;
23
using NUnit.Framework;
34
using NUnit.Framework.Legacy;
45
using System;
@@ -53,6 +54,26 @@ public void Should_Not_Invoke_SuccessValidationHandler_When_IsNotValid()
5354
Assert.That(result.IsValid, Is.False);
5455
}
5556

57+
[Test]
58+
public void Should_NotThrow_When_MembersAreNull()
59+
{
60+
var result = new ExpressValidatorBuilder<ObjWithTwoPublicProps>()
61+
.AddProperty(o => o.S)
62+
.WithValidation(o => o.MaximumLength(1))
63+
.AddField(o => o._sField)
64+
.WithValidation(o => o.MinimumLength(1))
65+
.Build()
66+
.Validate(new ObjWithTwoPublicProps());
67+
68+
var em1 = NullFallbackMessageProvider.GetMessage("S", new ValidationContext<string>(null));
69+
var em2 = NullFallbackMessageProvider.GetMessage("_sField", new ValidationContext<string>(null));
70+
71+
Assert.That(result.IsValid, Is.False);
72+
Assert.That(result.Errors.Count, Is.EqualTo(2));
73+
Assert.That(result.Errors[0].ErrorMessage, Is.EqualTo(em1));
74+
Assert.That(result.Errors[1].ErrorMessage, Is.EqualTo(em2));
75+
}
76+
5677
[Test]
5778
public void Should_Work_When_IsValid_ForSubObjWithSimpleConditionForComplexProperty()
5879
{
@@ -349,5 +370,25 @@ public void Should_AddFunc_Preserve_Property_Name(SetPropertyNameType setPropert
349370
Assert.That(result.Errors.FirstOrDefault().ErrorMessage, Does.Contain("TestName"));
350371
}
351372
}
373+
374+
[Test]
375+
[TestCase(true)]
376+
[TestCase(false)]
377+
public void Should_Validate_Primitive(bool valid)
378+
{
379+
int value;
380+
381+
if(valid)
382+
value = 3;
383+
else
384+
value = 1;
385+
386+
var result = new ExpressValidatorBuilder<int>()
387+
.AddFunc(i => i, "value")
388+
.WithValidation(o => o.GreaterThan(2))
389+
.BuildAndValidate(value);
390+
391+
Assert.That(result.IsValid, Is.EqualTo(valid));
392+
}
352393
}
353394
}

0 commit comments

Comments
 (0)