ResultR.Validation
1.0.1
dotnet add package ResultR.Validation --version 1.0.1
NuGet\Install-Package ResultR.Validation -Version 1.0.1
<PackageReference Include="ResultR.Validation" Version="1.0.1" />
<PackageVersion Include="ResultR.Validation" Version="1.0.1" />
<PackageReference Include="ResultR.Validation" />
paket add ResultR.Validation --version 1.0.1
#r "nuget: ResultR.Validation, 1.0.1"
#:package ResultR.Validation@1.0.1
#addin nuget:?package=ResultR.Validation&version=1.0.1
#tool nuget:?package=ResultR.Validation&version=1.0.1
ResultR.Validation
📖 Overview
Lightweight inline validation framework for ResultR. Define validation rules directly in your ValidateAsync() method using a fluent API, with seamless integration into ResultR's pipeline hooks.
What it does:
- Validates requests using a fluent API
- Integrates seamlessly with ResultR's pipeline hooks
- Returns
Result.Success()orResult.Failure()with aggregated errors
📋 Requirements
- .NET 10.0 or later
- C# 14.0 or later
📥 Installation
dotnet add package ResultR.Validation
🚀 Quick Start
using ResultR;
using ResultR.Validation;
public record CreateUserRequest(string Email, string Name, int Age) : IRequest<User>;
public class CreateUserHandler : IRequestHandler<CreateUserRequest, User>
{
public ValueTask<Result> ValidateAsync(CreateUserRequest request)
{
return Validator.For(request)
.RuleFor(x => x.Email)
.NotEmpty("Email is required")
.EmailAddress("Invalid email format")
.RuleFor(x => x.Name)
.NotEmpty("Name is required")
.MinLength(2, "Name must be at least 2 characters")
.MaxLength(100, "Name cannot exceed 100 characters")
.RuleFor(x => x.Age)
.GreaterThan(0, "Age must be positive")
.LessThanOrEqualTo(150, "Age must be realistic")
.ToResult();
}
public async ValueTask<Result<User>> HandleAsync(CreateUserRequest request, CancellationToken ct)
{
var user = new User(request.Email, request.Name, request.Age);
// Save user...
return Result<User>.Success(user);
}
}
Features
- Zero ceremony - No external validator classes, no DI registration for validators
- Inline validation - Define rules directly in
ValidateAsync()using a fluent API - Seamless integration - Works with ResultR's
IRequestHandler<TRequest>andIRequestHandler<TRequest, TResponse> - Automatic result conversion - Returns
Result.Success()orResult.Failure()with aggregated errors - Comprehensive built-in rules - String, numeric, collection, and custom validations
Built-in Validation Rules
String Validations
NotEmpty()- Ensures string is not null, empty, or whitespaceNotNull()- Ensures value is not nullMinLength(int)- Minimum string lengthMaxLength(int)- Maximum string lengthLength(int, int)- String length rangeMatches(string pattern)- Regex pattern matchingEmailAddress()- Valid email format
Numeric Validations
GreaterThan(T)- Value must be greater than comparisonGreaterThanOrEqualTo(T)- Value must be greater than or equal to comparisonLessThan(T)- Value must be less than comparisonLessThanOrEqualTo(T)- Value must be less than or equal to comparisonBetween(T, T)- Value must be within range (inclusive)
Collection Validations
NotEmpty()- Collection must contain at least one element
General Validations
NotNull()- Value must not be nullEqual(T)- Value must equal comparisonNotEqual(T)- Value must not equal comparisonMust(Func<T, bool>)- Custom predicate validation
Custom Validation Rules
Use the Must() method for custom validation logic:
public ValueTask<Result> ValidateAsync(CreateUserRequest request)
{
return Validator.For(request)
.RuleFor(x => x.Email)
.NotEmpty("Email is required")
.Must(email => email.EndsWith("@company.com"), "Must use company email")
.RuleFor(x => x.Name)
.Must(name => !name.Contains("admin", StringComparison.OrdinalIgnoreCase),
"Name cannot contain 'admin'")
.ToResult();
}
Accessing Validation Errors
When validation fails, errors are stored in the Result metadata:
var result = await mediator.SendAsync(request);
if (result.IsFailure)
{
var errors = result.GetMetadataValueOrDefault<List<ValidationError>>(ValidationMetadataKeys.ValidationErrors);
if (errors is not null)
{
foreach (var error in errors)
{
Console.WriteLine($"{error.PropertyName}: {error.ErrorMessage}");
}
}
}
// Example output:
// Email: Email is required
// Name: Name must be at least 2 characters
// Age: Age must be positive
How It Works
Validator.For(request)creates aValidationBuilder<T>for the request- Each
RuleFor()call selects a property and returns aRuleBuilder<T, TProperty> - Validation methods (e.g.,
NotEmpty(),MinLength()) add rules to an internal list ToResult()executes all rules and returns:Result.Success()if all rules passResult.Failure("Validation failed")with errors in metadata if any rule fails
❓ Why ResultR.Validation?
Unlike FluentValidation which requires separate validator classes and DI registration, ResultR.Validation lets you define validation rules inline within your handler's ValidateAsync() method. This reduces ceremony and keeps validation logic close to your business logic.
💬 Support
- Issues: GitHub Issues
- Documentation: GitHub Wiki
🔗 Links
🤝 Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
📄 License
ISC License - see the LICENSE file for details.
Built with ❤️ for the C# / DotNet community.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net10.0
- ResultR (>= 1.0.0)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.