Zentient Results Api Reference Result Builders Factories - ulfbou/Zentient.Results GitHub Wiki

🔧 Result Builders & Factories


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.

Success Factories

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.

Result.Success

Creates a successful result.

Result.Success()

  • Signature: public static Result Success()
  • Returns: An IResult instance representing success with ResultStatuses.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."));
    }

Result.Success(string message)

  • Signature: public static Result Success(string message)
  • Returns: An IResult instance representing success with ResultStatuses.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.");
    }

Result.Success<T>(T value)

  • Signature: public static Result<T> Success<T>(T value)
  • Returns: An IResult<T> instance representing success with ResultStatuses.Ok and the specified value.
  • 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());
    }

Result.Success<T>(T value, string message)

  • Signature: public static Result<T> Success<T>(T value, string message)
  • Returns: An IResult<T> instance representing success with ResultStatuses.Ok, the specified value, 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.");
    }

Result.Created

Creates a successful result indicating that a new resource was created.

Result.Created()

  • Signature: public static Result Created()
  • Returns: An IResult instance representing success with ResultStatuses.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();
    }

Result.Created<T>(T value)

  • Signature: public static Result<T> Created<T>(T value)
  • Returns: An IResult<T> instance representing success with ResultStatuses.Created and the newly created value.
  • 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);
    }

Result.NoContent

Creates a successful result indicating that the operation completed successfully but returned no content.

Result.NoContent()

  • Signature: public static Result NoContent()
  • Returns: An IResult instance representing success with ResultStatuses.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();
    }

Result.NoContent<T>()

  • Signature: public static Result<T> NoContent<T>()
  • Returns: An IResult<T> instance representing success with ResultStatuses.NoContent. The Value will be default(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>();
    }

Failure Factories

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.

Result.Failure

Creates a general failed result with one or more ErrorInfo objects.

Result.Failure(ErrorInfo error)

  • Signature: public static Result Failure(ErrorInfo error)
  • Returns: An IResult instance representing failure, with the provided ErrorInfo and ResultStatuses.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."));
    }

Result.Failure(IEnumerable<ErrorInfo> errors)

  • Signature: public static Result Failure(IEnumerable<ErrorInfo> errors)
  • Returns: An IResult instance representing failure, with the provided collection of ErrorInfo and ResultStatuses.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();
    }

Result.Failure<T>(ErrorInfo error)

  • Signature: public static Result<T> Failure<T>(ErrorInfo error)
  • Returns: An IResult<T> instance representing failure, with the provided ErrorInfo and ResultStatuses.InternalError as default status.
  • Purpose: For general, unspecified failures when a generic Result<T> is expected. The Value will be default(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));
        }
    }

Result.Failure<T>(IEnumerable<ErrorInfo> errors)

  • Signature: public static Result<T> Failure<T>(IEnumerable<ErrorInfo> errors)
  • Returns: An IResult<T> instance representing failure, with the provided collection of ErrorInfo and ResultStatuses.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 });
    }

Specific Categorized Failures

These factory methods provide convenience wrappers for common failure scenarios, automatically setting the appropriate ErrorCategory and IResultStatus.

Result.Validation

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.");
    }

Result.Unauthorized

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();
    }

Result.Forbidden

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();
    }

Result.NotFound

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);
    }

Result.Conflict

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();
    }

Result.InternalError

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}");
        }
    }
    

Result.FromException

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 or IResult<T> with an ErrorInfo generated from the exception. The ErrorInfo will have ErrorCategory.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);
        }
    }

Implicit Conversions

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.

T to Result<T>

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 simply return someValue; instead of return 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
    }

ErrorInfo to Result or Result<T>

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 concise return errorInfo; instead of return 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
    }
⚠️ **GitHub.com Fallback** ⚠️