Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
using ExpressValidator.Extensions.DependencyInjection;
using Shared;
using System.Reflection;

namespace ConfiguratorDemo
{
Expand All @@ -10,7 +9,7 @@ public static void Main(string[] args)
{
var builder = WebApplication.CreateBuilder(args);

builder.Services.AddExpressValidation(Assembly.GetExecutingAssembly());
builder.Services.AddExpressValidationFromCurrentAssembly();

builder.Services.AddTransient<IGuessTheNumberService, GuessTheNumberService>();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,12 @@
"ConfiguratorDemo": {
"commandName": "Project",
"launchBrowser": true,
"dotnetRunMessages": true,
"launchUrl": "guess",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
},
"applicationUrl": "https://localhost:56889;http://localhost:56890"
"applicationUrl": "http://localhost:5251"
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
},
"applicationUrl": "https://localhost:56889;http://localhost:56890"
"applicationUrl": "http://localhost:5251"
}
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
using ExpressValidator;
using System;

namespace Shared
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="ExpressValidator" Version="0.12.2" />
<PackageReference Include="ExpressValidator" Version="0.15.0" />
</ItemGroup>

</Project>
13 changes: 13 additions & 0 deletions src/ExpressValidator.Extensions.DependencyInjection/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,16 @@
## 0.5.0

- Add `IServiceCollection` extensions `AddExpressValidationFromAssemblyContaining<T>` and `AddExpressValidationFromCurrentAssembly`.
- Change `ValidatorConfigurator<T>` to inherit from `ValidationProfile<T>`.
- Update to ExpressValidator 0.15.0 and FluentValidation 12.1.1.
- Update ExpressValidator package for ExpressValidator.Extensions.DependencyInjection.Tests.
- Update ConfiguratorDemo sample to use `AddExpressValidationFromCurrentAssembly`.
- Unify applicationUrl in launchSettings.json for all samples.
- Update Shared.csproj to ExpressValidator 0.15.0.
- Update NUnit NuGet package to v4.6.0.
- Update Microsoft nuget packages.


## 0.4.0

- Introduced class-based validation configuration with dedicated configurator classes inheriting from `ValidatorConfigurator<T>` and registered through `AddExpressValidation`.
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.4.0</Version>
<Version>0.5.0</Version>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<Authors>Andrey Kolesnichenko</Authors>
<PackageLicenseExpression>MIT</PackageLicenseExpression>
Expand All @@ -15,15 +15,15 @@
<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.4.0.0</AssemblyVersion>
<AssemblyVersion>0.5.0.0</AssemblyVersion>
</PropertyGroup>

<ItemGroup Condition="'$(TargetFramework)' == 'netstandard2.0'">
<PackageReference Include="FluentValidation" Version="11.11.0" />
</ItemGroup>

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

<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
Expand All @@ -39,9 +39,9 @@
</ItemGroup>

<ItemGroup>
<PackageReference Include="ExpressValidator" Version="0.12.2" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="10.0.1" />
<PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="10.0.1" />
<PackageReference Include="ExpressValidator" Version="0.15.0" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="10.0.7" />
<PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="10.0.7" />
</ItemGroup>

<ItemGroup>
Expand Down
5 changes: 5 additions & 0 deletions src/ExpressValidator.Extensions.DependencyInjection/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,11 @@ builder.Services.AddTransient<IGuessTheNumberService, GuessTheNumberService>();
// ... (Other code omitted for brevity)
```

If you prefer not to pass an explicit `Assembly`, two convenience helpers are also available:

- `AddExpressValidationFromAssemblyContaining<T>()` - scans the assembly that contains `T`
- `AddExpressValidationFromCurrentAssembly()` - scans the calling assembly

## ⚙️ Validation with Options

In this approach, register an `IExpressValidatorBuilder<T, TOptions>` implementation (instead of `IExpressValidator<T>`) in the DI container by calling the `AddExpressValidatorBuilder` method.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ public static class ServiceCollectionExtensions
/// Registers all concrete, non-abstract, non-generic classes that inherit from <see cref="ValidatorConfigurator{T}"/>
/// into the Microsoft Dependency Injection container.
/// <br/>
/// Behind the scenes, for every configurator type <c>ExpressConfigurator&lt;T&gt;</c> found,
/// the DI container will also expose a proxy implementation of <see cref="IExpressValidator{T}"/>,
/// Behind the scenes, for every configurator type <c>ExpressConfigurator&lt;T&gt;</c> found,
/// the DI container will also expose a proxy implementation of <see cref="IExpressValidator{T}"/>,
/// enabling validation logic to be resolved transparently via the service provider.
/// </summary>
/// <param name="services">The <see cref="IServiceCollection"/> to add the services to.</param>
Expand All @@ -28,6 +28,31 @@ public static IServiceCollection AddExpressValidation(this IServiceCollection se
return services;
}

/// <summary>
/// Registers all concrete, non-abstract, non-generic classes that inherit from <see cref="ValidatorConfigurator{T}"/>
/// from the assembly that contains <typeparamref name="T"/> into the Microsoft Dependency Injection container.
/// </summary>
/// <typeparam name="T">A type located in the assembly to scan.</typeparam>
/// <param name="services">The <see cref="IServiceCollection"/> to add the services to.</param>
/// <param name="lifetime">The service lifetime for the registered configurators. Defaults to <see cref="ServiceLifetime.Transient"/>.</param>
/// <returns>The <see cref="IServiceCollection"/> for chaining.</returns>
public static IServiceCollection AddExpressValidationFromAssemblyContaining<T>(this IServiceCollection services, ServiceLifetime lifetime = ServiceLifetime.Transient)
{
return services.AddExpressValidation(typeof(T).Assembly, lifetime);
}

/// <summary>
/// Registers all concrete, non-abstract, non-generic classes that inherit from <see cref="ValidatorConfigurator{T}"/>
/// from the assembly that called this method into the Microsoft Dependency Injection container.
/// </summary>
/// <param name="services">The <see cref="IServiceCollection"/> to add the services to.</param>
/// <param name="lifetime">The service lifetime for the registered configurators. Defaults to <see cref="ServiceLifetime.Transient"/>.</param>
/// <returns>The <see cref="IServiceCollection"/> for chaining.</returns>
public static IServiceCollection AddExpressValidationFromCurrentAssembly(this IServiceCollection services, ServiceLifetime lifetime = ServiceLifetime.Transient)
{
return services.AddExpressValidation(Assembly.GetCallingAssembly(), lifetime);
}

public static IServiceCollection AddExpressValidator<T>(this IServiceCollection services, Action<ExpressValidatorBuilder<T>> configure, ServiceLifetime serviceLifetime = ServiceLifetime.Transient)
{
return AddExpressValidator(services, configure, new ExpressValidatorOptions() { OnFirstPropertyValidatorFailed = OnFirstPropertyValidatorFailed.Continue }, serviceLifetime);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
namespace ExpressValidator.Extensions.DependencyInjection
{
public abstract class ValidatorConfigurator<T> : IValidatorConfigurator<T>
public abstract class ValidatorConfigurator<T> : ValidationProfile<T>, IValidatorConfigurator<T>
{
private readonly ExpressValidatorBuilder<T> _validatorBuilder;
protected ValidatorConfigurator(ExpressValidatorOptions expressValidatorOptions = null)
Expand All @@ -9,8 +9,6 @@ protected ValidatorConfigurator(ExpressValidatorOptions expressValidatorOptions
_validatorBuilder = new ExpressValidatorBuilder<T>(expressValidatorOptions.OnFirstPropertyValidatorFailed);
}

public abstract void Configure(ExpressValidatorBuilder<T> expressValidatorBuilder);

IExpressValidator<T> IValidatorConfigurator<T>.Build()
{
Configure(_validatorBuilder);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,11 @@ builder.Services.AddTransient<IGuessTheNumberService, GuessTheNumberService>();
// ... (Other code omitted for brevity)
```

If you want a shorthand for assembly scanning, you can also use:

- `AddExpressValidationFromAssemblyContaining<T>()`
- `AddExpressValidationFromCurrentAssembly()`

## Validation with Options

In this approach, register an `IExpressValidatorBuilder<T, TOptions>` implementation (instead of `IExpressValidator<T>`) in the DI container by calling the `AddExpressValidatorBuilder` method.
Expand Down Expand Up @@ -254,4 +259,4 @@ public class ReloadableNumberGuessingService : IReloadableNumberGuessingService

## Samples

See samples folder for concrete example.
See samples folder for concrete example.
4 changes: 2 additions & 2 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 Down
Original file line number Diff line number Diff line change
Expand Up @@ -147,8 +147,8 @@ public void Should_ThrowException_WhenConfiguratorDoesNotExist()
_services.AddExpressValidation(Assembly.GetExecutingAssembly());
_serviceProvider = _services.BuildServiceProvider();

Assert.Throws<InvalidOperationException>(() =>
_serviceProvider.GetRequiredService<IExpressValidator<NonExistentModel>>());
Assert.Throws<InvalidOperationException>((Action)(() =>
_serviceProvider.GetRequiredService<IExpressValidator<NonExistentModel>>()));
}

[Test]
Expand Down Expand Up @@ -241,7 +241,7 @@ public void Should_DisposeProperlyInScope_WhenScopedLifetimeUsed()
}

// Validator should have been disposed with scope
Assert.DoesNotThrow(() => validator.Validate(new TestPersonModel()));
Assert.DoesNotThrow((Action)(() => validator.Validate(new TestPersonModel())));
}

[Test]
Expand Down
Loading
Loading