API Reference ResultExtensions - ulfbou/Zentient.Results GitHub Wiki
-
Namespace:
Zentient.Results
-
Type:
Static Class
-
Summary: Provides a rich set of extension methods for
IResult
andIResult<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
IResult
was successful. -
Parameters:
-
result
(this IResult
): The result instance.
-
-
Return Value: (
bool
):true
if the result is successful; otherwise,false
. -
Behavior: Returns
true
if the result's status code is in the 2xx range and itsErrors
collection 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
IResult
failed. -
Parameters:
-
result
(this IResult
): The result instance.
-
-
Return Value: (
bool
):true
if the result is a failure; otherwise,false
. -
Behavior: Returns
true
if the result's status code is not in the 2xx range OR itsErrors
collection 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
): TheErrorCategory
to check for.
-
-
Return Value: (
bool
):true
if 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
):true
if 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
IResult
is successful, then returns the original result. -
Parameters:
-
result
(this IResult
): The result instance. -
onSuccess
(Action
): The action to execute.
-
-
Return Value: (
IResult
): The originalIResult
instance. - 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
IResult
is 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 originalIResult
instance. - 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>
withtrue
if the original result was successful, or a failedIResult<bool>
withfalse
and 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
ErrorInfo
objects 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
ErrorInfo
in 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
Errors
collection. -
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 offunc
if the current result is successful, or the original failed result if it was a failure. -
Behavior: This method allows for sequential execution of
IResult
operations. If the initialresult
is a failure,func
is 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 offunc
if the currentIResult
is 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
ResultException
if theIResult
is 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.IsFailure
istrue
). - 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
):true
if successful; otherwise,false
. -
Remarks: This is an overload for type inference. Functionally identical to the non-generic
IsSuccess
but 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
):true
if 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
Tap
onIResult<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 typeTOut
if 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
result
is a success,selector
is applied toresult.Value
and a new successfulResult<TOut>
is returned. Ifresult
is a failure,selector
is 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 aTask
of a generic result.
-
-
Return Value: (
Task<IResult<TOut>>
): ATask
that resolves to the result ofnext
if the current result is successful, or aTask
that 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-genericIResult
is 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>
): ATask
that resolves to the result ofnext
if the current result is successful, or aTask
that resolves to the original failedIResult
if 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
Value
from 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.IsFailure
istrue
, 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
Value
or returns a specified default value if theIResult<TValue>
is a failure or itsValue
isnull
. -
Parameters:
-
result
(this IResult<TValue>
): The generic result instance. -
defaultValue
(TValue
): The value to return if the result is a failure or itsValue
isnull
.
-
-
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 offunc
if the currentIResult<TIn>
is successful, or the original failedIResult
if 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 offunc
if the currentIResult<TIn>
is successful, or a failedIResult<TOut>
propagating the original errors and status if it was a failure. -
Behavior: An alternative to
Map
orBind
for 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
ResultExtensions
are 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
Result
andResult<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