Skip to main content

Validate / ValidateAsync

Validate checks a condition on the success value. If the condition is false, the Result becomes a failure. The value itself is not changed — it either passes through or gets replaced by an error.

Signatures

// Sync — fixed error
Result<T> Validate<T>(this Result<T> result, Func<T, bool> predicate, Error errorIfFalse)

// Sync — error computed from the value
Result<T> Validate<T>(this Result<T> result, Func<T, bool> predicate, Func<T, Error> errorFactory)

// Async
Task<Result<T>> ValidateAsync<T>(this Task<Result<T>> resultTask, Func<T, bool> predicate, Error errorIfFalse)
Task<Result<T>> ValidateAsync<T>(this Task<Result<T>> resultTask, Func<T, Task<bool>> predicate, Error errorIfFalse)
Task<Result<T>> ValidateAsync<T>(this Result<T> result, Func<T, Task<bool>> predicate, Error errorIfFalse)

When to use

Use Validate for business rules that can be expressed as boolean conditions:

  • Is the user active?
  • Is the order total positive?
  • Does the user have permission?

Basic example

public Result<User> EnsureUserCanLogin(User user)
{
return Result.Success(user)
.Validate(u => u.IsActive,
Error.Validation("Account is disabled", "USER_INACTIVE"))
.Validate(u => !u.IsLocked,
Error.Validation("Account is locked", "USER_LOCKED"))
.Validate(u => u.EmailConfirmed,
Error.Validation("Email not confirmed", "EMAIL_NOT_CONFIRMED"));
}

If the first validation fails, the remaining ones are skipped (short-circuit behavior).

Dynamic error messages

Use the error factory overload to include the value in the error:

.Validate(
order => order.Total > 0,
order => Error.Validation($"Order total must be positive, got {order.Total}", "INVALID_TOTAL")
)

In a pipeline

public async Task<Result<Order>> PlaceOrder(PlaceOrderRequest request)
{
return await Result.Start(request)
.Validate(r => r.Items.Count > 0,
Error.Validation("Order must have items", "NO_ITEMS"))
.Validate(r => r.Items.Count <= 100,
Error.Validation("Too many items", "TOO_MANY_ITEMS"))
.ThenAsync(r => CreateOrderAsync(r));
}

Async validation

When the check itself is asynchronous (e.g. checking a database):

var result = await Result.Start(request)
.ValidateAsync(
async r => await _db.Users.AnyAsync(u => u.Email == r.Email),
Error.NotFound("User not found", "USER_NOT_FOUND"));

Validate vs Then

ValidateThen
Changes the value?NoYes
ReturnsSame Result<T>Result<TOut>
Use forGuard conditionsOperations that produce a new value

See also