Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
af7aa8e
Added an optional `Action<T>` `onSuccessValidation` parameter to `Exp…
kolan72 Nov 28, 2025
d4d9f76
Merge pull request #67 from kolan72/dev-di
kolan72 Jan 13, 2026
7458722
Remove unnecessary using.
kolan72 Jan 13, 2026
ac4a88a
Package 0.4.0 version and update CHANGELOG.md.
kolan72 Jan 13, 2026
46e7956
Refactor `ExpressPropertyValidator<TObj, T>` to set `IsAsync` in the …
kolan72 Jan 19, 2026
40a8b7c
Add internal `PropertyValidationProcessor<TObj, T>` class.
kolan72 Jan 19, 2026
454bb4b
Switch to using `PropertyValidationProcessor<TObj, T>` in `ExpressPro…
kolan72 Jan 22, 2026
ad64720
Introduce internal utility `TypeInfo<T>` class and use it in `TypeVal…
kolan72 Jan 29, 2026
67b1d7b
Replace `TypeInfo<T>` with `TypeTraits<T>`.
kolan72 Jan 30, 2026
3f028bc
Add internal utility `TypeHelper<T>` class with `IsNull` method and …
kolan72 Feb 4, 2026
a834eac
Introduce abstract `ValidatorProfile<T>` class.
kolan72 Feb 5, 2026
fb957c3
Rename `ValidatorProfile<T>` to `ValidationProfile<T>`.
kolan72 Feb 10, 2026
86e0a25
Update to FluentValidation 12.1.1.
kolan72 Feb 10, 2026
23c07f7
Add `TypeName` property to `TypeTraits<T>`.
kolan72 Feb 18, 2026
c4fd4fc
Add internal static `ValidationFallbackProvider` class representing v…
kolan72 Feb 19, 2026
7e8ae7d
Use `ValidationFallbackProvider` for `null` validation in `ExpressVal…
kolan72 Feb 19, 2026
9c7f062
Use `ValidationFallbackProvider` for `null` validation in `ExpressVal…
kolan72 Feb 20, 2026
a6a942b
Check async validation before null validation in `ExpressValidator<TO…
kolan72 Feb 26, 2026
b98c8c0
Improve test coverage for null handling.
kolan72 Feb 26, 2026
cc85c0b
Simplify `ValidationProfile<T>`.
kolan72 Feb 26, 2026
f3f33d4
Update to System.ValueTuple 4.6.2.
kolan72 Feb 27, 2026
5a82ae0
Merge pull request #68 from kolan72/main
kolan72 Mar 2, 2026
9ac5147
Add `ExpressValidator.Tests.Net8.csproj` for testing `ExpressValidato…
kolan72 Mar 5, 2026
551cef6
Merge branch 'dev' of https://github.com/kolan72/ExpressValidator int…
kolan72 Mar 5, 2026
c380303
Update NuGet.md
kolan72 Mar 6, 2026
f20e8bb
Fix test issues in ExpressValidator.Tests.Net8.
kolan72 Mar 6, 2026
8797ddd
Update NuGet.md and README.md.
kolan72 Mar 8, 2026
50fa99c
Refactor `ExpressValidator<TObj, TOptions>`.
kolan72 Mar 9, 2026
091cbcb
Refactor `ExpressValidatorBuilder<TObj, TOptions>.Build` to remove de…
kolan72 Mar 10, 2026
2d2c22f
Deprecate the `ExpressValidator<TObj, TOptions>` class.
kolan72 Mar 10, 2026
b1eb32d
Merge pull request #69 from kolan72/dev
kolan72 Mar 11, 2026
64b6bf3
Package 0.15.0 version and update CHANGELOG.md.
kolan72 Mar 11, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,23 @@
## 0.15.0

- Introduced validation failure for `null` root instances instead of throwing FluentValidation exceptions.
- Introduce abstract `ValidationProfile<T>` class.
- Added an optional `Action<T>` `onSuccessValidation` parameter to `ExpressValidatorBuilder<TObj, TOptions>.AddFunc` for handling successful validation of `Func` result.
- Refactor `ExpressValidatorBuilder<TObj, TOptions>.Build` to remove dependency on `ExpressValidator<TObj, TOptions>`.
- Deprecate the `ExpressValidator<TObj, TOptions>` class.
- Add internal `PropertyValidationProcessor<TObj, T>` class.
- Switch to using `PropertyValidationProcessor<TObj, T>` in `ExpressPropertyValidator<TObj, T>` and `ExpressPropertyValidator<TObj, TOptions, T>`.
- Refactor `ExpressPropertyValidator<TObj, T>` to set `IsAsync` in the constructor.
- Add internal utility `TypeHelper<T>` class with `IsNull` method and use it in `TypeValidatorBase<T>`.
- Introduce internal utility `TypeTraits<T>` class.
- Update to FluentValidation 12.1.1.
- Add internal static `ValidationFallbackProvider` class representing validation failure for a `null` instance.
- Add ExpressValidator.Tests.Net8.csproj for testing ExpressValidator when TargetFramework is net8.0.
- Update tests to System.ValueTuple 4.6.2.
- Improve test coverage for null handling.
- Update NuGet.md and README.md.


## 0.12.2

- 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.
Expand Down
6 changes: 6 additions & 0 deletions ExpressValidator.sln
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ExpressValidator.Tests", "t
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Benchmark", "bench\Benchmark\Benchmark.csproj", "{163D81E7-128E-431F-B827-F2AA2BD1D077}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ExpressValidator.Tests.Net8", "tests\ExpressValidator.Tests.Net8\ExpressValidator.Tests.Net8.csproj", "{C74789B4-A028-45FC-9771-F5848491AB89}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand All @@ -27,6 +29,10 @@ Global
{163D81E7-128E-431F-B827-F2AA2BD1D077}.Debug|Any CPU.Build.0 = Debug|Any CPU
{163D81E7-128E-431F-B827-F2AA2BD1D077}.Release|Any CPU.ActiveCfg = Release|Any CPU
{163D81E7-128E-431F-B827-F2AA2BD1D077}.Release|Any CPU.Build.0 = Release|Any CPU
{C74789B4-A028-45FC-9771-F5848491AB89}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{C74789B4-A028-45FC-9771-F5848491AB89}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C74789B4-A028-45FC-9771-F5848491AB89}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C74789B4-A028-45FC-9771-F5848491AB89}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ ExpressValidator is a library that provides the ability to validate objects usin
- Supports adding a property or field for validation.
- Verifies that a property expression is a property and a field expression is a field, and throws `ArgumentException` if it is not.
- Supports adding a `Func` that provides a value for validation.
- Provides quick and easy validation using `QuickValidator`, with built-in tolerance for `null` values.
- Built-in `null` tolerance - `null` root instances fail validation instead of throwing exceptions.
- Quick and easy validation with `QuickValidator`, with robust support for `null` values.
- Supports asynchronous validation.
- Targets .NET Standard 2.0+

Expand Down
11 changes: 11 additions & 0 deletions src/ExpressValidator.Extensions.DependencyInjection/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,14 @@
## 0.4.0

- Introduced class-based validation configuration with dedicated configurator classes inheriting from `ValidatorConfigurator<T>` and registered through `AddExpressValidation`.
- Update to ExpressValidator 0.12.2 and FluentValidation 12.1.0.
- Update Microsoft nuget packages.
- Edit NuGet README.
- Edit README.md.
- Add Shared.csproj to the ExpressValidator.Extensions.DependencyInjection.Sample.sln solution.
- Split sample project into multiple projects illustrating README-described features.


## 0.3.12

- Support .NET 8.0 and FluentValidation 12.0.0.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
<PropertyGroup>
<TargetFrameworks>netstandard2.0;net8.0</TargetFrameworks>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<Version>0.3.12</Version>
<Version>0.4.0</Version>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<Authors>Andrey Kolesnichenko</Authors>
<PackageLicenseExpression>MIT</PackageLicenseExpression>
Expand All @@ -15,7 +15,7 @@
<PackageTags>FluentValidation Validation DependencyInjection</PackageTags>
<Description>The ExpressValidator.Extensions.DependencyInjection package extends ExpressValidator to provide integration with Microsoft Dependency Injection.</Description>
<Copyright>Copyright 2024 Andrey Kolesnichenko</Copyright>
<AssemblyVersion>0.3.12.0</AssemblyVersion>
<AssemblyVersion>0.4.0.0</AssemblyVersion>
</PropertyGroup>

<ItemGroup Condition="'$(TargetFramework)' == 'netstandard2.0'">
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,12 @@
using FluentValidation.Results;
using Microsoft.Extensions.DependencyInjection;
using System;
using System.Collections.Generic;
using System.Data;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace ExpressValidator.Extensions.DependencyInjection
{
internal class ProxyValidator<T> : IExpressValidator<T>
internal class ProxyValidator<T> : IExpressValidator<T>
{
private readonly IExpressValidator<T> _innerValidator;
public ProxyValidator(IServiceProvider serviceProvider)
Expand Down
10 changes: 7 additions & 3 deletions src/ExpressValidator/ExpressValidator.TOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,20 +12,24 @@ namespace ExpressValidator
/// </summary>
/// <typeparam name="TObj"></typeparam>
/// <typeparam name="TOptions"></typeparam>
#pragma warning disable S1133 // Deprecated code should be removed
[Obsolete("The ExpressValidator<TObj, TOptions> class is obsolete and will be removed in a future version.")]
#pragma warning restore S1133 // Deprecated code should be removed
public class ExpressValidator<TObj, TOptions> : IExpressValidator<TObj>
{
private readonly IEnumerable<IObjectValidator<TObj, TOptions>> _validators;
private readonly OnFirstPropertyValidatorFailed _validationMode;
private readonly IEnumerable<IObjectValidator<TObj>> _validators;

internal ExpressValidator(TOptions options, IEnumerable<IObjectValidator<TObj, TOptions>> validators, OnFirstPropertyValidatorFailed validationMode)
{
_validators = validators;
_validationMode = validationMode;

foreach (var validator in _validators)
foreach (var validator in validators)
{
validator.ApplyOptions(options);
}

_validators = validators;
}

public ValidationResult Validate(TObj obj)
Expand Down
8 changes: 8 additions & 0 deletions src/ExpressValidator/ExpressValidator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,19 @@ public ValidationResult Validate(TObj obj)
{
throw new InvalidOperationException($"Object validator has a property or field with asynchronous validation rules. Please use {nameof(ValidateAsync)} method.");
}
if (TypeHelper<TObj>.IsNull(obj))
{
return ValidationFallbackProvider.GetNullFailure<TObj>();
}
return _validationMode == OnFirstPropertyValidatorFailed.Break ? ValidateWithBreak(obj) : ValidateWithContinue(obj);
}

public Task<ValidationResult> ValidateAsync(TObj obj, CancellationToken token = default)
{
if (TypeHelper<TObj>.IsNull(obj))
{
return Task.FromResult(ValidationFallbackProvider.GetNullFailure<TObj>());
}
return _validationMode == OnFirstPropertyValidatorFailed.Break ? ValidateWithBreakAsync(obj, token) : ValidateWithContinueAsync(obj, token);
}

Expand Down
12 changes: 9 additions & 3 deletions src/ExpressValidator/ExpressValidator.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
<PropertyGroup>
<TargetFrameworks>netstandard2.0;net8.0</TargetFrameworks>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<Version>0.12.2</Version>
<Version>0.15.0</Version>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<Authors>Andrey Kolesnichenko</Authors>
<Description>ExpressValidator is a library that provides the ability to validate objects using the FluentValidation library, but without object inheritance from `AbstractValidator`.</Description>
Expand All @@ -15,7 +15,7 @@
<PackageIcon>ExpressValidator.png</PackageIcon>
<PackageReadmeFile>NuGet.md</PackageReadmeFile>
<PackageIconUrl />
<AssemblyVersion>0.12.2.0</AssemblyVersion>
<AssemblyVersion>0.15.0.0</AssemblyVersion>
<FileVersion>0.0.0.0</FileVersion>
</PropertyGroup>

Expand All @@ -24,7 +24,7 @@
</ItemGroup>

<ItemGroup Condition="'$(TargetFramework)' == 'net8.0'">
<PackageReference Include="FluentValidation" Version="12.1.0" />
<PackageReference Include="FluentValidation" Version="12.1.1" />
</ItemGroup>

<ItemGroup>
Expand All @@ -51,6 +51,12 @@
</AssemblyAttribute>
</ItemGroup>

<ItemGroup>
<AssemblyAttribute Include="System.Runtime.CompilerServices.InternalsVisibleToAttribute">
<_Parameter1>ExpressValidator.Tests.Net8</_Parameter1>
</AssemblyAttribute>
</ItemGroup>

<ItemGroup>
<None Include="..\..\ExpressValidator.png">
<Pack>True</Pack>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,16 @@ internal class ExpressPropertyValidator<TObj, TOptions, T> : IExpressPropertyVal

private Action<IRuleBuilderOptions<T, T>> _action;

public ExpressPropertyValidator(Func<TObj, T> propertyFunc, string propName, bool isAsync)
private readonly Action<T> _onSuccessValidation;

private PropertyValidationProcessor<TObj, T> _validationProcessor;

public ExpressPropertyValidator(Func<TObj, T> propertyFunc, string propName, bool isAsync, Action<T> onSuccessValidation = null)
{
_propertyFunc = propertyFunc;
_propName = propName;
IsAsync = isAsync;
_onSuccessValidation = onSuccessValidation;
}

public void SetValidation(Action<TOptions, IRuleBuilderOptions<T, T>> action)
Expand All @@ -31,16 +36,12 @@ public void SetValidation(Action<TOptions, IRuleBuilderOptions<T, T>> action)

public Task<(bool IsValid, List<ValidationFailure> Failures)> ValidateAsync(TObj obj, CancellationToken token = default)
{
return _typeValidator.ValidateExAsync(_propertyFunc(obj), token);
return _validationProcessor.ValidateAsync(obj, token);
}

public (bool IsValid, List<ValidationFailure> Failures) Validate(TObj obj)
{
if (IsAsync)
{
throw new InvalidOperationException();
}
return _typeValidator.ValidateEx(_propertyFunc(obj));
return _validationProcessor.Validate(obj);
}

public void ApplyOptions(TOptions options)
Expand All @@ -60,6 +61,7 @@ private void SetTypeValidator()
_typeValidator = new TypeValidator<T>();
}
_typeValidator.SetValidation(_action, _propName);
_validationProcessor = new PropertyValidationProcessor<TObj, T>(_propertyFunc, _typeValidator, _onSuccessValidation);
}

public bool IsAsync { get; }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,62 +12,34 @@ internal class ExpressPropertyValidator<TObj, T> : IExpressPropertyValidator<TOb
private readonly string _propName;
private readonly TypeValidatorBase<T> _typeValidator;
private readonly Func<TObj, T> _propertyFunc;

private readonly Action<T> _onSuccessValidation;
private PropertyValidationProcessor<TObj, T> _validationProcessor;

public ExpressPropertyValidator(Func<TObj, T> propertyFunc, string propName, TypeValidatorBase<T> typeValidator, Action<T> onSuccessValidation = null)
{
_propertyFunc = propertyFunc;
_propName = propName;
_typeValidator = typeValidator;
IsAsync = _typeValidator.IsAsync == true;
_onSuccessValidation = onSuccessValidation;
}

public void SetValidation(Action<IRuleBuilderOptions<T, T>> action)
{
_typeValidator.SetValidation(action, _propName);
_validationProcessor = new PropertyValidationProcessor<TObj, T>(_propertyFunc, _typeValidator, _onSuccessValidation);
}

public async Task<(bool IsValid, List<ValidationFailure> Failures)> ValidateAsync(TObj obj, CancellationToken token = default)
public Task<(bool IsValid, List<ValidationFailure> Failures)> ValidateAsync(TObj obj, CancellationToken token = default)
{
if (_onSuccessValidation != null)
{
var value = _propertyFunc(obj);
var res = await _typeValidator.ValidateExAsync(value, token);
if (res.IsValid)
{
_onSuccessValidation(value);
}
return res;
}
else
{
return await _typeValidator.ValidateExAsync(_propertyFunc(obj), token);
}
return _validationProcessor.ValidateAsync(obj, token);
}

public (bool IsValid, List<ValidationFailure> Failures) Validate(TObj obj)
{
if (IsAsync)
{
throw new InvalidOperationException();
}
if (_onSuccessValidation != null)
{
var value = _propertyFunc(obj);
var res = _typeValidator.ValidateEx(value);
if (res.IsValid)
{
_onSuccessValidation(value);
}
return res;
}
else
{
return _typeValidator.ValidateEx(_propertyFunc(obj));
}
return _validationProcessor.Validate(obj);
}

public bool IsAsync => _typeValidator.IsAsync == true;
public bool IsAsync { get; }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
using FluentValidation.Results;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using System.Threading;

namespace ExpressValidator
{
internal class PropertyValidationProcessor<TObj, T>
{
private readonly TypeValidatorBase<T> _typeValidator;
private readonly Func<TObj, T> _propertyFunc;
private readonly bool _isAsync;
private readonly Action<T> _onSuccessValidation;

public PropertyValidationProcessor(Func<TObj, T> propertyFunc, TypeValidatorBase<T> typeValidator, Action<T> onSuccessValidation)
{
_propertyFunc = propertyFunc;
_typeValidator = typeValidator;
_isAsync = _typeValidator.IsAsync == true;
_onSuccessValidation = onSuccessValidation;
}

public async Task<(bool IsValid, List<ValidationFailure> Failures)> ValidateAsync(TObj obj, CancellationToken token = default)
{
if (_onSuccessValidation != null)
{
var value = _propertyFunc(obj);
var res = await _typeValidator.ValidateExAsync(value, token);
if (res.IsValid)
{
_onSuccessValidation(value);
}
return res;
}
else
{
return await _typeValidator.ValidateExAsync(_propertyFunc(obj), token);
}
}

public (bool IsValid, List<ValidationFailure> Failures) Validate(TObj obj)
{
if (_isAsync)
{
throw new InvalidOperationException();
}
if (_onSuccessValidation != null)
{
var value = _propertyFunc(obj);
var res = _typeValidator.ValidateEx(value);
if (res.IsValid)
{
_onSuccessValidation(value);
}
return res;
}
else
{
return _typeValidator.ValidateEx(_propertyFunc(obj));
}
}
}
}
Loading
Loading