The .NET
library for fluent, expressive, and clean validation using the best of multiple patterns: Guard
, Result<T>
, and extensible validators with localization support built-in.
https://www.nuget.org/packages/CleanValidation.Core
Install-Package CleanValidation.Core
- β
Fluent validation syntax using
Guard
- β
Built-in
Result<T>
system:SuccessResult
,ErrorResult
,InvalidResult
,ConflictResult
, etc. - β
Separate
Guard
andGuardThrow
for result-based or exception-based flows - β
Extensible
IValidator<T>
interface for custom validations - β
Localization support via
cultureName
- β Fully composable, testable, and DI-ready
public record User(long Id, string Name, byte Age, long UserTypeId, string Description);
public class UserValidator(IUserTypeRepository userTypeRepository) : Validator<User>
{
private readonly IUserTypeRepository _userTypeRepository = userTypeRepository;
public override async Task<IResult> ValidateAsync(
User value,
string cultureName = "en-US",
CancellationToken cancellationToken = default)
{
var result = await base.ValidateAsync(value, cultureName, cancellationToken);
if (!result.Success)
return result;
var exists = await _userTypeRepository.ExistsAsync(value.UserTypeId, cancellationToken);
if (!exists)
return NotFoundResult.Create(new Error(message: "Invalid user type id", field: nameof(user.UserTypeId)));
return Guard.Create()
.AgainstNullOrWhiteSpace(value.Name)
.AgainstNullOrWhiteSpace(value.Description)
.GetResult();
}
}
Fluent and chainable validation that returns a Result<T>
:
Guard.Create()
.AgainstNull(dto)
.AgainstNullOrWhiteSpace(dto.Name)
.GetResult();
Same as Guard
, but throws exceptions instead:
GuardThrow.Create()
.AgainstNull(entity)
.AgainstNullOrWhiteSpace(entity.Name);
All results implement IResult
or IResult<T>
:
SuccessResult<T>
ErrorResult<T>
InvalidResult<T>
ConflictResult<T>
NotFoundResult<T>
ProblemResult<T>
Each provides fluent creation with error details and optional field association.
All methods that return or create results accept a cultureName
parameter, used internally with .resx
resources to localize messages dynamically.
Guard.Create("pt-BR")
.AgainstNullOrWhiteSpace(dto.Descricao)
.GetResult();
- Error β Base error model with
Message
andField
- ErrorUtils β Factory methods for consistent error creation
- CleanValidationException β Base exception with helper
ThrowIfNull
,ThrowIfNullOrWhiteSpace
, etc. - Guard β Fluent builder that returns results
- GuardThrow β Fluent builder that throws exceptions
- IResult / IResult β Interfaces for all result types
- Validator / IValidator β Abstract class + interface for async or sync custom validation logic
- ResultExtensions β Helpful extensions like
ErrorsTo<T>()
- Minimalism + Expressiveness
- Strong typing, fail-fast, and readability
- Plug-and-play usability
- Clear separation between structural validation and business rules
- Encouragement of clean architecture without forcing opinionated structures
- Check out
CleanValidation.Extensions.Http
for ASP.NET Core integration - Use
CleanValidation.DependencyInjection
to register allIValidator<T>
via Scrutor
This library combines the clarity of Guard
clauses, the safety of Result<T>
, and the convenience of fluent interfaces β all wrapped with localization and extensibility in mind.
CleanValidation: The .NET library for Clean Validations with clarity, consistency, and control. π
ASP.NET integration for CleanValidation.
This package provides seamless mapping from IResult
/ IResult<T>
(from CleanValidation.Core
) into ASP.NET Core IActionResult
types, enabling fluent and expressive return statements in your controller or endpoint layer.
- β
Maps
SuccessResult<T>
toOkObjectResult
- β
Maps
ErrorResult<T>
toBadRequestObjectResult
- β
Maps
InvalidResult<T>
toUnprocessableEntityObjectResult
- β
Supports
ConflictResult<T>
,NotFoundResult<T>
,ProblemResult<T>
- β
Simple
.ToActionResult()
extension method for allIResult
- β Consistent HTTP responses for clean architecture applications
https://www.nuget.org/packages/CleanValidation.Extensions.Http
Install-Package CleanValidation.Extensions.Http
using CleanValidation.Extensions.Http;
User user = new("LΓvia", 21, "Lady");
IResult<User> result = SuccessResult<User>.Create(user);
IActionResult actionResult = result.ToActionResult();
return result.ToActionResult();
This will automatically return:
Ok(user)
if result is aSuccessResult<T>
BadRequest(...)
if result is anErrorResult<T>
UnprocessableEntity(...)
if result is anInvalidResult<T>
Conflict(...)
if result is aConflictResult<T>
NotFound(...)
if result is aNotFoundResult<T>
Problem(...)
if result is aProblemResult<T>
- Makes controllers and endpoints cleaner and less error-prone
- Eliminates repetitive response handling logic
- Promotes consistency across all HTTP layers
- Fits perfectly into CQRS, Minimal APIs, and RESTful patterns
User user = new("LΓvia", 21, "Lady");
IResult<User> result = SuccessResult<User>.Create(user);
IActionResult actionResult = result.ToActionResult();
Assert.NotNull(actionResult);
Assert.IsType<OkObjectResult>(actionResult);
Use CleanValidation.Extensions.Http to make your ASP.NET Core apps fluent, expressive, and consistent from validation to response.
Plug-and-play integration for CleanValidation with ASP.NET Dependency Injection, using Scrutor
under the hood to automatically register validators.
- β
Auto-registers all
IValidator<T>
implementations asScoped
- β
Easy-to-use extension method:
AddCleanValidation(...)
- β Supports multiple assemblies or a single reference
- β No configuration required β just plug and go
https://www.nuget.org/packages/CleanValidation.DependencyInjection
Install-Package CleanValidation.DependencyInjection
using CleanValidation.DependencyInjection;
var services = new ServiceCollection();
services.AddCleanValidation(typeof(UserValidator).Assembly);
Or for multiple assemblies:
services.AddCleanValidation(
typeof(UserValidator).Assembly,
typeof(OtherValidator).Assembly
);
This extension method uses Scrutor
to scan the specified assemblies and register all types that implement:
IValidator<T>
public class UserValidator : Validator<User>
{
public override Task<IResult> ValidateAsync(User value, string cultureName = "en-US", CancellationToken cancellationToken = default)
{
return Task.FromResult(Guard.Create(cultureName)
.AgainstNullOrWhiteSpace(value.Name)
.AgainstNullOrWhiteSpace(value.Description)
.GetResult());
}
}
Then simply inject it:
public class UserHandler
{
private readonly IValidator<User> _validator;
public UserHandler(IValidator<User> validator)
{
_validator = validator;
}
public async Task<IResult> HandleAsync(User user)
{
return await _validator.ValidateAsync(user);
}
}
CleanValidation.DependencyInjection makes your validators discoverable, injectable, and production-ready β instantly.
Package | Description |
---|---|
CleanValidation.Core |
Fluent validation with Guard + Result |
CleanValidation.Extensions.Http |
HTTP mapping of results to IActionResult |
CleanValidation.DependencyInjection |
Registers IValidator<T> via Scrutor |