API Reference ResultExtensions - ulfbou/Zentient.Results GitHub Wiki
-
Namespace:
Zentient.Results -
Type:
Static Class -
Summary: Provides a rich set of extension methods for
IResultandIResult<T>, significantly enhancing the library's fluent API for composing operations, handling outcomes, and simplifying common result-related tasks.
ResultExtensions centralizes utility functions that operate on IResult and IResult<T> instances. This approach keeps the core Result types lean while providing powerful, readable, and composable operations. It encourages a declarative style of programming by allowing developers to chain actions and transformations on results, reducing the need for verbose if/else checks and promoting a more functional approach to error handling.
-
Signature:
public static bool IsSuccess(this IResult result) -
Summary: Checks if the operation represented by the
IResultwas successful. -
Parameters:
-
result(this IResult): The result instance.
-
-
Return Value: (
bool):trueif the result is successful; otherwise,false. -
Behavior: Returns
trueif the result's status code is in the 2xx range and itsErrorscollection is empty. -
Example Usage:
IResult opResult = SomeService.PerformAction(); if (opResult.IsSuccess()) { Console.WriteLine("Operation succeeded!"); }
-
Signature:
public static bool IsFailure(this IResult result) -
Summary: Checks if the operation represented by the
IResultfailed. -
Parameters:
-
result(this IResult): The result instance.
-
-
Return Value: (
bool):trueif the result is a failure; otherwise,false. -
Behavior: Returns
trueif the result's status code is not in the 2xx range OR itsErrorscollection contains one or more entries. It's the logical opposite ofIsSuccess(). -
Example Usage:
IResult opResult = SomeService.PerformAction(); if (opResult.IsFailure()) { Console.WriteLine($"Operation failed: {opResult.Error}"); }
-
Signature:
public static bool HasErrorCategory(this IResult result, ErrorCategory category) -
Summary: Determines if a failed result contains at least one error belonging to a specific
ErrorCategory. -
Parameters:
-
result(this IResult): The result instance. -
category(ErrorCategory): TheErrorCategoryto check for.
-
-
Return Value: (
bool):trueif the result is a failure and has an error with the specified category; otherwise,false. -
Example Usage:
IResult userOp = UserService.UpdateUser(userDto); if (userOp.HasErrorCategory(ErrorCategory.Validation)) { Console.WriteLine("User update failed due to validation errors."); }
-
Signature:
public static bool HasErrorCode(this IResult result, string errorCode) - Summary: Determines if a failed result contains at least one error with a specific error code.
-
Parameters:
-
result(this IResult): The result instance. -
errorCode(string): The error code to check for.
-
-
Return Value: (
bool):trueif the result is a failure and has an error with the specified code; otherwise,false. -
Example Usage:
IResult authResult = AuthService.Login("user", "pass"); if (authResult.HasErrorCode("InvalidCredentials")) { Console.WriteLine("Invalid username or password."); }
-
Signature:
public static IResult OnSuccess(this IResult result, Action onSuccess) -
Summary: Executes a side-effecting action if the
IResultis successful, then returns the original result. -
Parameters:
-
result(this IResult): The result instance. -
onSuccess(Action): The action to execute.
-
-
Return Value: (
IResult): The originalIResultinstance. - Behavior: Useful for logging, auditing, or other actions that should only happen upon success without modifying the result itself.
-
Example Usage:
IResult saveResult = DataService.SaveData(data); saveResult.OnSuccess(() => Console.WriteLine("Data saved successfully.")) .OnFailure(errors => Console.WriteLine($"Failed to save: {errors[0].Message}"));
-
Signature:
public static IResult OnFailure(this IResult result, Action<IReadOnlyList<ErrorInfo>> onFailure) -
Summary: Executes a side-effecting action if the
IResultis a failure, then returns the original result. -
Parameters:
-
result(this IResult): The result instance. -
onFailure(Action<IReadOnlyList<ErrorInfo>>): The action to execute, receiving the list of errors.
-
-
Return Value: (
IResult): The originalIResultinstance. - Behavior: Useful for logging errors, displaying messages, or other actions specific to failure.
-
Example Usage:
IResult processResult = Processor.Process(); processResult.OnFailure(errors => { foreach (var err in errors) Console.WriteLine($"Error: {err.Message}"); });
-
Signature:
public static IResult<bool> ToBoolResult(this IResult result) -
Summary: Converts an
IResult(non-generic) into anIResult<bool>. -
Parameters:
-
result(this IResult): The non-generic result.
-
-
Return Value: (
IResult<bool>): A successfulIResult<bool>withtrueif the original result was successful, or a failedIResult<bool>withfalseand propagating the original errors and status if it was a failure. -
Example Usage:
IResult voidOp = Database.DeleteUser(123); IResult<bool> boolResult = voidOp.ToBoolResult(); if (boolResult.IsSuccess && boolResult.Value) Console.WriteLine("User deleted successfully!");
-
Signature:
public static string ToErrorString(this IResult result, string separator = "; ") -
Summary: Concatenates the messages of all
ErrorInfoobjects in a failed result into a single string. -
Parameters:
-
result(this IResult): The result instance. -
separator(string): The string to use as a separator between error messages. Defaults to "; ".
-
-
Return Value: (
string): A combined string of error messages if the result is a failure; otherwise, an empty string. -
Example Usage:
IResult loginResult = AuthService.Login("bad", "credentials"); if (loginResult.IsFailure()) { Console.WriteLine($"Login errors: {loginResult.ToErrorString()}"); }
-
Signature:
public static string? FirstErrorMessage(this IResult result) -
Summary: Retrieves the message of the first
ErrorInfoin a failed result, ornull. -
Parameters:
-
result(this IResult): The result instance.
-
-
Return Value: (
string?): The message of the first error if the result is a failure and has errors; otherwise,null. -
Behavior: This provides a quick way to get a single error message for simple display, without direct access to the
Errorscollection. -
Example Usage:
IResult purchaseResult = Store.CompletePurchase(item); if (purchaseResult.IsFailure()) { Console.WriteLine($"Purchase failed: {purchaseResult.FirstErrorMessage()}"); }
-
Signature:
public static IResult Then(this IResult result, Func<IResult> func) -
Summary: Chains another operation that returns a non-generic
IResult, executing it only if the current result is successful. -
Parameters:
-
result(this IResult): The preceding result. -
func(Func<IResult>): The function representing the next operation.
-
-
Return Value: (
IResult): The result offuncif the current result is successful, or the original failed result if it was a failure. -
Behavior: This method allows for sequential execution of
IResultoperations. If the initialresultis a failure,funcis not executed, and the failure is propagated. -
Example Usage:
IResult step1 = Service.DoStep1(); IResult finalResult = step1.Then(() => Service.DoStep2()); // DoStep2 only runs if DoStep1 succeeded
-
Signature:
public static IResult<TOut> Then<TOut>(this IResult result, Func<IResult<TOut>> func) -
Summary: Chains another operation that returns a generic
IResult<TOut>, executing it only if the current non-generic result is successful. -
Parameters:
-
result(this IResult): The preceding non-generic result. -
func(Func<IResult<TOut>>): The function representing the next operation that produces a value.
-
-
Return Value: (
IResult<TOut>): The result offuncif the currentIResultis successful, or a failedIResult<TOut>propagating the original errors and status if it was a failure. -
Behavior: Similar to
Then(Func<IResult>), but for chaining to an operation that returns a value. -
Example Usage:
IResult voidOp = UserService.PerformSetup(); IResult<User> getUserResult = voidOp.Then(() => UserService.GetUser("new-user")); // GetUser only runs if setup succeeded
-
Signature:
public static void ThrowIfFailure(this IResult result) -
Summary: Throws a
ResultExceptionif theIResultis a failure. -
Parameters:
-
result(this IResult): The result instance.
-
- Behavior: This method provides an "escape hatch" from the Result pattern. It's typically used at the application's "edge" (e.g., in an API controller or the main application entry point) when a failure is deemed unrecoverable at that level and should escalate to an exception.
-
Exceptions Thrown:
ResultException(ifresult.IsFailureistrue). - Remarks: Use sparingly and intentionally. The strength of the Result pattern lies in explicit non-exception-based error handling.
-
Example Usage:
public IActionResult CreateUser([FromBody] UserDto userDto) { IResult<User> result = _userService.CreateUser(userDto); try { result.ThrowIfFailure(); // If result is a failure, ResultException is thrown return Ok(result.Value); } catch (ResultException ex) { // Map ResultException.Errors to ProblemDetails for API response return BadRequest(new { errors = ex.Errors.Select(e => e.Message) }); } }
-
Signature:
public static bool IsSuccess<TValue>(this IResult<TValue> result) -
Summary: Checks if the generic
IResult<TValue>was successful. -
Parameters:
-
result(this IResult<TValue>): The generic result instance.
-
-
Return Value: (
bool):trueif successful; otherwise,false. -
Remarks: This is an overload for type inference. Functionally identical to the non-generic
IsSuccessbut provides explicit type safety for generic results.
-
Signature:
public static bool IsFailure<TValue>(this IResult<TValue> result) -
Summary: Checks if the generic
IResult<TValue>failed. -
Parameters:
-
result(this IResult<TValue>): The generic result instance.
-
-
Return Value: (
bool):trueif a failure; otherwise,false. -
Remarks: Overload for type inference. Functionally identical to the non-generic
IsFailure.
-
Signature:
public static IResult<TValue> OnSuccess<TValue>(this IResult<TValue> result, Action<TValue> onSuccess) -
Summary: Executes a side-effecting action if the generic
IResult<TValue>is successful, passing theValue, then returns the original result. -
Parameters:
-
result(this IResult<TValue>): The generic result instance. -
onSuccess(Action<TValue>): The action to execute, receiving the successful value.
-
-
Return Value: (
IResult<TValue>): The originalIResult<TValue>instance. -
Remarks: Functionally identical to
TaponIResult<TValue>. -
Example Usage:
IResult<string> loadedData = DataRepository.LoadString(); loadedData.OnSuccess(data => Console.WriteLine($"Loaded: {data}")) .OnFailure(errors => Console.WriteLine($"Failed to load: {errors.First().Message}"));
-
Signature:
public static IResult<TValue> OnFailure<TValue>(this IResult<TValue> result, Action<IReadOnlyList<ErrorInfo>> onFailure) -
Summary: Executes a side-effecting action if the generic
IResult<TValue>is a failure, then returns the original result. -
Parameters:
-
result(this IResult<TValue>): The generic result instance. -
onFailure(Action<IReadOnlyList<ErrorInfo>>): The action to execute, receiving the list of errors.
-
-
Return Value: (
IResult<TValue>): The originalIResult<TValue>instance. -
Example Usage:
IResult<User> fetchedUser = UserService.FetchUser(userId); fetchedUser.OnFailure(errors => Log.Error($"User fetch failed: {errors.ToErrorString()}"));
-
Signature:
public static IResult<TOut> Map<TIn, TOut>(this IResult<TIn> result, Func<TIn, TOut> selector) -
Summary: Transforms the successful value of an
IResult<TIn>into a new typeTOutif the result is successful. -
Parameters:
-
result(this IResult<TIn>): The source generic result. -
selector(Func<TIn, TOut>): A function to apply to the successfulValue.
-
-
Return Value: (
IResult<TOut>): A newIResult<TOut>with the transformed value on success, or a propagated failure. -
Behavior: If the input
resultis a success,selectoris applied toresult.Valueand a new successfulResult<TOut>is returned. Ifresultis a failure,selectoris skipped, and a new failedResult<TOut>is returned with the original errors and status. -
Example Usage:
IResult<Product> productResult = ProductService.GetProduct(productId); IResult<ProductDto> productDtoResult = productResult.Map(product => new ProductDto(product));
-
Signature:
public static async Task<IResult<TOut>> Bind<TIn, TOut>(this IResult<TIn> result, Func<TIn, Task<IResult<TOut>>> next) -
Summary: Asynchronously chains an operation that returns a
Task<IResult<TOut>>, executing it only if the currentIResult<TIn>is successful. -
Parameters:
-
result(this IResult<TIn>): The preceding generic result. -
next(Func<TIn, Task<IResult<TOut>>>): An asynchronous function representing the next operation, taking the successful value and returning aTaskof a generic result.
-
-
Return Value: (
Task<IResult<TOut>>): ATaskthat resolves to the result ofnextif the current result is successful, or aTaskthat resolves to a failedIResult<TOut>propagating the original errors and status if it was a failure. - Behavior: This enables seamless chaining of asynchronous operations where each step might fail.
-
Example Usage:
// Assume GetUserAsync and SaveUserAsync return Task<IResult<T>> IResult<User> initialUserResult = await UserRepository.GetUserAsync(userId); IResult<User> updatedUserResult = await initialUserResult .Bind(async user => { user.Status = UserStatus.Active; return await UserRepository.SaveUserAsync(user); });
-
Signature:
public static async Task<IResult> Bind(this IResult result, Func<Task<IResult>> next) -
Summary: Asynchronously chains an operation that returns a
Task<IResult>, executing it only if the current non-genericIResultis successful. -
Parameters:
-
result(this IResult): The preceding non-generic result. -
next(Func<Task<IResult>>): An asynchronous function representing the next operation.
-
-
Return Value: (
Task<IResult>): ATaskthat resolves to the result ofnextif the current result is successful, or aTaskthat resolves to the original failedIResultif it was a failure. - Behavior: Enables asynchronous sequencing of non-value-producing operations that might fail.
-
Example Usage:
IResult validationResult = await Validator.ValidateInputAsync(input); IResult processingResult = await validationResult.Bind(async () => await Processor.ProcessDataAsync(input));
-
Signature:
public static TValue Unwrap<TValue>(this IResult<TValue> result) -
Summary: Retrieves the successful
Valuefrom anIResult<TValue>. If the result is a failure, it throws anInvalidOperationException. -
Parameters:
-
result(this IResult<TValue>): The generic result instance.
-
-
Return Value: (
TValue): The successful value. -
Exceptions Thrown:
InvalidOperationException(ifresult.IsFailureistrue, with a message detailing the errors). -
Behavior: A less flexible alternative to
GetValueOrThrow(), provided for convenience when immediate unwrapping is desired. -
Example Usage:
IResult<string> data = FetchData(); try { string value = data.Unwrap(); // Throws if FetchData() returned a failure Console.WriteLine(value); } catch (InvalidOperationException ex) { Console.WriteLine($"Error unwrapping: {ex.Message}"); }
-
Signature:
public static TValue GetValueOrDefault<TValue>(this IResult<TValue> result, TValue defaultValue) -
Summary: Retrieves the successful
Valueor returns a specified default value if theIResult<TValue>is a failure or itsValueisnull. -
Parameters:
-
result(this IResult<TValue>): The generic result instance. -
defaultValue(TValue): The value to return if the result is a failure or itsValueisnull.
-
-
Return Value: (
TValue): The successful value, or thedefaultValue. - Behavior: Useful for scenarios where you always need a value, even if the primary operation failed, avoiding exceptions.
-
Example Usage:
IResult<int> countResult = Database.GetItemCount(); int count = countResult.GetValueOrDefault(0); // If GetItemCount fails, count will be 0
-
Signature:
public static IResult Then<TIn>(this IResult<TIn> result, Func<TIn, IResult> func) -
Summary: Chains another operation that returns a non-generic
IResult, executing it only if the currentIResult<TIn>is successful. -
Parameters:
-
result(this IResult<TIn>): The preceding generic result. -
func(Func<TIn, IResult>): The function representing the next non-generic operation, taking the successful value.
-
-
Return Value: (
IResult): The result offuncif the currentIResult<TIn>is successful, or the original failedIResultif it was a failure. -
Behavior: Similar to
Bind, but specifically for chaining from a generic result to a non-generic result. -
Example Usage:
IResult<UserData> userData = AuthProvider.Login(credentials); IResult processResult = userData.Then(user => ReportingService.LogLogin(user.Id));
-
Signature:
public static IResult<TOut> Then<TIn, TOut>(this IResult<TIn> result, Func<TIn, IResult<TOut>> func) -
Summary: Chains another operation that returns a generic
IResult<TOut>, executing it only if the currentIResult<TIn>is successful. -
Parameters:
-
result(this IResult<TIn>): The preceding generic result. -
func(Func<TIn, IResult<TOut>>): The function representing the next generic operation, taking the successful value.
-
-
Return Value: (
IResult<TOut>): The result offuncif the currentIResult<TIn>is successful, or a failedIResult<TOut>propagating the original errors and status if it was a failure. -
Behavior: An alternative to
MaporBindfor chaining generic results, often used for clarity. -
Example Usage:
IResult<Order> orderResult = OrderProcessor.CreateOrder(cart); IResult<PaymentConfirmation> paymentResult = orderResult.Then(order => PaymentGateway.ProcessPayment(order.Amount));
-
Fluent API: The methods in
ResultExtensionsare designed to be chainable, enabling a highly readable and concise style for expressing complex workflows that involve multiple operations with potential failure points. -
Separation of Concerns: These extensions keep the core
ResultandResult<T>structs focused on their fundamental immutability and state, while providing a rich functional API for interaction. - Common Patterns: This class encapsulates many common patterns for working with the Result type, reducing boilerplate and promoting consistency across your codebase.
Last Updated: 2025-06-08 Version: 0.3.0