**Lightweight result wrapper, validation and transformation toolkit for C# **
Brings clean Result<T>
handling and extensible validation with custom attributes.
- ✅ Consistent
Result<T>
response pattern - ✅ One-line
.Validate()
extension for request models - ✅
.Transform()
extension for clean and safe object/result projection - ✅ Rich built-in and custom validation attributes
- ✅ Works great with System.ComponentModel.Annotations
- ✅ Validation for nested objects
dotnet add package Routya.ResultKit --version 1.0.2
using Routya.ResultKit.Attributes;
using System.ComponentModel.DataAnnotations;
private enum UserRole { Admin, User, Guest }
private class TestModel
{
[Required]
public string Name { get; set; }
[EmailAddress]
public string Email { get; set; }
[Range(18, 120)]
public int Age { get; set; }
[StringEnum(typeof(UserRole))]
public string Role { get; set; }
[Required]
public string Password { get; set; }
[Compare("Password")]
public string ConfirmPassword { get; set; }
public decimal MinPurchase { get; set; }
[GreaterThan("MinPurchase")]
public decimal MaxPurchase { get; set; }
}
app.MapPost("/users", (CreateUserRequest request) =>
{
var validationResult = request.Validate();
if (!validationResult.Success)
return Results.BadRequest(validationResult);
return Results.Ok(Result.Ok(new { Id = 1 }));
});
{
"Success": true,
"Data": {
"Name": "Henry",
"Email": "[email protected]",
"Age": 30,
"Role": "Admin",
"Password": "abc123",
"ConfirmPassword": "abc123",
"MinPurchase": 100.0,
"MaxPurchase": 200.0
},
"Error": null
}
{
"Success": false,
"Data": null,
"Error": {
"Title": "Validation Failed",
"Status": 400,
"Extensions": {
"errors": {
"Name": [
"The Name field is required."
],
"Email": [
"The Email field is not a valid e-mail address."
],
"Age": [
"The field Age must be between 18 and 120."
],
"Role": [
"Role must be one of: Admin, User, Guest"
],
"ConfirmPassword": [
"'ConfirmPassword' and 'Password' do not match."
],
"MaxPurchase": [
"MaxPurchase must be greater than MinPurchase"
]
}
}
}
}
Routya.ResultKit includes powerful validation attributes ready to use:
Attribute | Purpose |
---|---|
[GreaterThan("OtherProp")] |
Ensure a property is greater than another |
[LessThan("OtherProp")] |
Ensure a property is less than another |
[RequiredIf("OtherProp", "Value")] |
Conditionally require a property |
[RequiredIfEmpty("OtherProp")] |
Require a property if another is empty |
[StringEnum(typeof(EnumType))] |
Ensure a string matches an Enum name |
[MatchRegex("pattern")] |
Validate a string against a regex |
[MinItems(count)] |
Validate minimum items in a collection |
[MaxItems(count)] |
Validate maximum items in a collection |
[ValidStartEndDateRange("Start", "End")] |
Validate that StartDate is before EndDate (This is a class level attribute) |
[ValidDateTimeOffsetRange("End")] |
Validate DateTimeOffset ranges |
[ValidDateTimeRange("End")] |
Validate DateTime ranges |
Use .Transform(...)
to reshape validated models or result data into domain entities or response objects — cleanly and safely.
var request = "Hello";
var greeting = request.Transform(str => new Greeting
{
Message = str,
Length = str.Length
});
public class Greeting
{
public string Message { get; set; }
public int Length { get; set; }
}
var result = request.Validate()
.Transform(req => new CreateUserCommand
{
Name = req.Name,
Email = req.Email,
Role = Enum.Parse<UserRole>(req.Role, ignoreCase: true)
});
var result = Result.Ok(user)
.Transform(u => new UserResponse
{
Id = u.Id,
Name = u.Name
});
Benefit | Description |
---|---|
✅ Fluent | Clean chaining after .Validate() |
✅ Safe | When using Result it only transforms data if result is successful |
✅ Expressive | Encourages intentional mapping logic |
✅ Lightweight | Zero dependencies, pure functional mapping |
TOut Transform<TIn, TOut>(this TIn input, Func<TIn, TOut> selector)
Result<TOut> Transform<TIn, TOut>(this Result<TIn> result, Func<TIn, TOut> selector)