Zentient Results Api Reference Result Builders Factories - ulfbou/Zentient.Results GitHub Wiki
Zentient.Results emphasizes immutability and explicit outcomes. Because of this, you typically don't create Result
or Result<T>
instances using their constructors directly. Instead, you use a set of static factory methods on the Result
class (for both generic and non-generic results) and implicit conversions.
These builders and factories simplify the creation of Result
objects, ensuring consistency and proper initialization, whether the operation succeeded or failed.
These methods allow you to create instances of IResult
or IResult<T>
that represent a successful operation. They often map to standard HTTP success codes like 200 OK, 201 Created, or 204 No Content.
Creates a successful result.
-
Signature:
public static Result Success()
-
Returns: An
IResult
instance representing success withResultStatuses.Ok
. - Purpose: To indicate that an operation without a specific return value completed successfully.
-
Example:
using Zentient.Results; public IResult DeleteUser(int userId) { // ... logic to delete user if (userDeleted) { return Result.Success(); // Operation completed successfully } return Result.NotFound(new ErrorInfo("UserNotFound", "User not found.")); }
-
Signature:
public static Result Success(string message)
-
Returns: An
IResult
instance representing success withResultStatuses.Ok
and an associated message. - Purpose: To provide additional context or information for a successful operation.
-
Example:
public IResult UpdateSettings(string newConfig) { // ... logic to update settings return Result.Success("Settings updated successfully."); }
-
Signature:
public static Result<T> Success<T>(T value)
-
Returns: An
IResult<T>
instance representing success withResultStatuses.Ok
and the specifiedvalue
. - Purpose: To indicate that an operation completed successfully and produced a specific value.
-
Example:
public IResult<User> GetUser(int id) { // ... logic to retrieve user User? user = userRepository.GetById(id); if (user != null) { return Result.Success(user); // Successfully retrieved user } return Result.NotFound<User>("User", id.ToString()); }
-
Signature:
public static Result<T> Success<T>(T value, string message)
-
Returns: An
IResult<T>
instance representing success withResultStatuses.Ok
, the specifiedvalue
, and an associated message. - Purpose: To provide both a successful value and additional context.
-
Example:
public IResult<Report> GenerateReport(Guid reportId) { // ... logic to generate report Report report = new Report { Id = reportId, Status = "Completed" }; return Result.Success(report, "Report generated and is ready for download."); }
Creates a successful result indicating that a new resource was created.
-
Signature:
public static Result Created()
-
Returns: An
IResult
instance representing success withResultStatuses.Created
(HTTP 201). - Purpose: Typically used in RESTful API contexts when a new resource is successfully added.
-
Example:
public IResult CreateOrder(OrderData data) { // ... save order to database return Result.Created(); }
-
Signature:
public static Result<T> Created<T>(T value)
-
Returns: An
IResult<T>
instance representing success withResultStatuses.Created
and the newly createdvalue
. - Purpose: To return the newly created resource along with the 201 Created status.
-
Example:
public IResult<Product> AddNewProduct(Product newProduct) { // ... save product, assign ID newProduct.Id = Guid.NewGuid(); return Result.Created(newProduct); }
Creates a successful result indicating that the operation completed successfully but returned no content.
-
Signature:
public static Result NoContent()
-
Returns: An
IResult
instance representing success withResultStatuses.NoContent
(HTTP 204). - Purpose: Used for operations where the response body should be empty, often after a successful delete or update operation where no new resource is returned.
-
Example:
public IResult ClearCache() { // ... clear cache return Result.NoContent(); }
-
Signature:
public static Result<T> NoContent<T>()
-
Returns: An
IResult<T>
instance representing success withResultStatuses.NoContent
. TheValue
will bedefault(T)
. -
Purpose: Less common, but useful if a generic
IResult<T>
signature is required but the operation legitimately returns no content. -
Example:
public IResult<string> ProcessQueueItem(int itemId) { // If processing succeeds, but there's no meaningful string to return // You might opt for Result.NoContent<string>() or just Result.Success<string>(string.Empty) // depending on whether the empty string is a valid "content". return Result.NoContent<string>(); }
These methods allow you to create instances of IResult
or IResult<T>
that represent a failed operation, often categorized by the type of error and corresponding to standard HTTP error codes.
Creates a general failed result with one or more ErrorInfo
objects.
-
Signature:
public static Result Failure(ErrorInfo error)
-
Returns: An
IResult
instance representing failure, with the providedErrorInfo
andResultStatuses.InternalError
(HTTP 500) as default status. -
Purpose: For general, unspecified failures where a specific
ErrorInfo
is available. -
Example:
using Zentient.Results; public IResult AuthenticateUser(string username, string password) { // ... authentication logic return Result.Failure(new ErrorInfo(ErrorCategory.Authentication, "InvalidCredentials", "Username or password is incorrect.")); }
-
Signature:
public static Result Failure(IEnumerable<ErrorInfo> errors)
-
Returns: An
IResult
instance representing failure, with the provided collection ofErrorInfo
andResultStatuses.InternalError
as default status. - Purpose: To report multiple errors simultaneously (e.g., from multiple validation checks).
-
Example:
public IResult ProcessBatch(IEnumerable<string> items) { var errors = new List<ErrorInfo>(); // ... process items, add errors to list if (errors.Any()) { return Result.Failure(errors); } return Result.Success(); }
-
Signature:
public static Result<T> Failure<T>(ErrorInfo error)
-
Returns: An
IResult<T>
instance representing failure, with the providedErrorInfo
andResultStatuses.InternalError
as default status. -
Purpose: For general, unspecified failures when a generic
Result<T>
is expected. TheValue
will bedefault(T)
. -
Example:
public IResult<List<Product>> GetAllProducts() { try { // ... database call throw new Exception("Database connection lost."); } catch (Exception ex) { return Result.Failure<List<Product>>(new ErrorInfo(ErrorCategory.InternalError, "DbError", ex.Message)); } }
-
Signature:
public static Result<T> Failure<T>(IEnumerable<ErrorInfo> errors)
-
Returns: An
IResult<T>
instance representing failure, with the provided collection ofErrorInfo
andResultStatuses.InternalError
as default status. -
Purpose: To report multiple errors for a generic
Result<T>
. -
Example:
public IResult<User> RegisterUser(UserRegistrationDto dto) { var validationErrors = new List<ErrorInfo>(); // ... perform complex validation, add multiple errors if found if (validationErrors.Any()) { return Result.Failure<User>(validationErrors); } // ... successful registration return Result.Success(new User { Id = Guid.NewGuid(), Email = dto.Email }); }
These factory methods provide convenience wrappers for common failure scenarios, automatically setting the appropriate ErrorCategory
and IResultStatus
.
Creates a failed result indicating that validation rules were violated. Corresponds to ResultStatuses.BadRequest
(HTTP 400).
-
Signatures:
public static Result Validation(string code, string message, IDictionary<string, object>? parameters = null)
public static Result Validation(ErrorInfo error)
public static Result Validation(IEnumerable<ErrorInfo> errors)
public static Result<T> Validation<T>(string code, string message, IDictionary<string, object>? parameters = null)
public static Result<T> Validation<T>(ErrorInfo error)
public static Result<T> Validation<T>(IEnumerable<ErrorInfo> errors)
- Purpose: To report errors related to invalid input, data format, or business rule violations during validation.
-
Example:
using Zentient.Results; public IResult<string> ValidateEmail(string email) { if (string.IsNullOrWhiteSpace(email)) { return Result.Validation<string>("EmailEmpty", "Email cannot be empty."); } if (!email.Contains("@")) { return Result.Validation<string>("EmailFormat", "Invalid email format."); } return Result.Success("Email is valid."); }
Creates a failed result indicating that the client is not authenticated. Corresponds to ResultStatuses.Unauthorized
(HTTP 401).
-
Signatures:
public static Result Unauthorized(string code, string message)
public static Result Unauthorized(ErrorInfo error)
public static Result<T> Unauthorized<T>(string code, string message)
public static Result<T> Unauthorized<T>(ErrorInfo error)
- Purpose: For situations where a request lacks valid authentication credentials.
-
Example:
public IResult GetSensitiveData(string token) { if (string.IsNullOrEmpty(token) || !IsValidToken(token)) { return Result.Unauthorized("MissingToken", "Authentication token is missing or invalid."); } // ... retrieve data return Result.Success(); }
Creates a failed result indicating that the client is authenticated but lacks authorization to perform the action. Corresponds to ResultStatuses.Forbidden
(HTTP 403).
-
Signatures:
public static Result Forbidden(string code, string message)
public static Result Forbidden(ErrorInfo error)
public static Result<T> Forbidden<T>(string code, string message)
public static Result<T> Forbidden<T>(ErrorInfo error)
- Purpose: For authorization failures (e.g., user doesn't have the necessary role or permission).
-
Example:
public IResult AdminAction(User currentUser) { if (!currentUser.HasRole("Admin")) { return Result.Forbidden("InsufficientPermissions", "You do not have administrative privileges."); } // ... perform admin action return Result.Success(); }
Creates a failed result indicating that the requested resource was not found. Corresponds to ResultStatuses.NotFound
(HTTP 404).
-
Signatures:
public static Result NotFound(string resourceName, string? identifier = null)
public static Result NotFound(ErrorInfo error)
public static Result<T> NotFound<T>(string resourceName, string? identifier = null)
public static Result<T> NotFound<T>(ErrorInfo error)
- Purpose: For resource not found scenarios (e.g., a specific user, product, or document).
-
Example:
public IResult<Order> GetOrderById(Guid orderId) { Order? order = orderRepository.Get(orderId); if (order == null) { return Result.NotFound<Order>("Order", orderId.ToString()); } return Result.Success(order); }
Creates a failed result indicating a conflict with the current state of the resource. Corresponds to ResultStatuses.Conflict
(HTTP 409).
-
Signatures:
public static Result Conflict(string code, string message, IDictionary<string, object>? parameters = null)
public static Result Conflict(ErrorInfo error)
public static Result<T> Conflict<T>(string code, string message, IDictionary<string, object>? parameters = null)
public static Result<T> Conflict<T>(ErrorInfo error)
- Purpose: For concurrency conflicts, attempting to create a duplicate resource that must be unique, or invalid state transitions.
-
Example:
public IResult CreateUniqueUser(string username) { if (userRepository.Exists(username)) { return Result.Conflict("UserExists", $"Username '{username}' is already taken."); } // ... create user return Result.Created(); }
Creates a failed result indicating an unexpected internal server error or unhandled exception. Corresponds to ResultStatuses.InternalError
(HTTP 500).
-
Signatures:
public static Result InternalError(string code, string message, IDictionary<string, object>? parameters = null)
public static Result InternalError(ErrorInfo error)
public static Result<T> InternalError<T>(string code, string message, IDictionary<string, object>? parameters = null)
public static Result<T> InternalError<T>(ErrorInfo error)
- Purpose: For uncaught exceptions, critical system failures, or errors that are not expected client-side.
-
Example:
public IResult ProcessPayment(decimal amount) { try { // ... call external payment gateway // Assume gateway throws unexpected error throw new Exception("Payment gateway unresponsive."); } catch (Exception ex) { return Result.InternalError("PaymentGatewayFailure", $"Unhandled error during payment: {ex.Message}"); } }
Creates a failed result directly from an Exception
. This is a convenient way to convert traditional exceptions into a Result
format.
-
Signatures:
public static Result FromException(Exception ex)
public static Result<T> FromException<T>(Exception ex)
-
Returns: A failed
IResult
orIResult<T>
with anErrorInfo
generated from the exception. TheErrorInfo
will haveErrorCategory.InternalError
, a code based on the exception type, and the exception's message. -
Purpose: For easily converting caught exceptions into
Result
failures, useful at integration points or top-level error handling. -
Example:
public IResult<User> LoadUserConfig(string userId) { try { // ... code that might throw User user = LoadFromDisk(userId); return Result.Success(user); } catch (FileNotFoundException ex) { // Specific handling for known exception return Result.NotFound<User>("UserConfig", userId); } catch (Exception ex) { // Catch-all for other unexpected exceptions return Result.FromException<User>(ex); } }
For enhanced fluency and brevity, Zentient.Results provides implicit operators that allow certain types to be directly returned as Result
or Result<T>
instances, converting them automatically into successful or failed results.
A value of type T
can implicitly convert to a successful Result<T>
.
-
Purpose: This is incredibly convenient for methods that return
IResult<T>
, allowing you to simplyreturn someValue;
instead ofreturn Result.Success(someValue);
. -
Example:
using Zentient.Results; public IResult<int> Divide(int a, int b) { if (b == 0) { return Result.Validation<int>("DivideByZero", "Cannot divide by zero."); } return a / b; // Implicitly converts to Result<int>.Success(a / b) } // Usage: IResult<int> result = Divide(10, 2); if (result.IsSuccess) { Console.WriteLine($"Result: {result.Value}"); // Output: Result: 5 }
An ErrorInfo
instance (or a collection of them) can implicitly convert to a failed Result
or Result<T>
.
-
Purpose: Simplifies returning a failure result when you have an
ErrorInfo
ready, allowing for concisereturn errorInfo;
instead ofreturn Result.Failure(errorInfo);
. -
Example:
using Zentient.Results; using System.Collections.Generic; public IResult ProcessFile(string fileName) { if (!File.Exists(fileName)) { return new ErrorInfo(ErrorCategory.NotFound, "FileNotFound", $"File '{fileName}' does not exist."); // Implicitly converts to Result.Failure } // ... process file return Result.Success(); } public IResult<List<string>> ParseData(string rawData) { if (string.IsNullOrWhiteSpace(rawData)) { return Result.Validation<List<string>>("EmptyData", "Input data cannot be empty."); } var errors = new List<ErrorInfo>(); // ... parsing logic, add errors if (errors.Any()) { return errors; // Implicitly converts to Result<List<string>>.Failure } return new List<string> { "parsed", "data" }; // Implicitly converts to Result<List<string>>.Success }