DevJoy.Result 0.9.0

dotnet add package DevJoy.Result --version 0.9.0
                    
NuGet\Install-Package DevJoy.Result -Version 0.9.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="DevJoy.Result" Version="0.9.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="DevJoy.Result" Version="0.9.0" />
                    
Directory.Packages.props
<PackageReference Include="DevJoy.Result" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add DevJoy.Result --version 0.9.0
                    
#r "nuget: DevJoy.Result, 0.9.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package DevJoy.Result@0.9.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=DevJoy.Result&version=0.9.0
                    
Install as a Cake Addin
#tool nuget:?package=DevJoy.Result&version=0.9.0
                    
Install as a Cake Tool

DevJoy.Result

A robust, lightweight Result pattern implementation for .NET that eliminates null reference exceptions and provides elegant error handling through functional programming principles.

๐Ÿš€ Features

  • Type-Safe Error Handling: Replace exceptions with explicit Result types
  • Railway Pattern: Chain operations safely without null checks
  • Multiple Error Support: Collect and propagate multiple validation errors
  • Monadic Operations: Functional programming patterns with Then() and Transform()
  • Rich Error Context: Structured error codes with user and technical messages
  • Zero Dependencies: Lightweight library with no external dependencies

๐Ÿ“ฆ Installation

dotnet add package DevJoy.Result

๐ŸŽฏ Quick Start

Creating Results

// Success case
var success = Result<string>.Success("Hello World");

// Single error
var failure = Result<string>.Failure(
    new Error(ErrorCode.BadRequest, "Invalid input"));

// Multiple errors (validation)
var errors = new[]
{
    new Error(ErrorCode.ParameterMissing, "Name is required"),
    new Error(ErrorCode.ParameterOutOfRange, "Age must be positive")
};
var multipleErrors = Result<User>.Failure(errors);

Chaining Operations

var result = GetUser(userId)                    // Returns Result<User>
    .Then(ValidateUser)                         // Chain validation (can fail)
    .Transform(user => user.Name.ToUpper())     // Safe transformation  
    .Transform(name => $"Hello, {name}!")       // Safe formatting
    .Then(SaveGreeting);                        // Chain save operation (can fail)

// Check the final result
if (result.IsSuccess)
{
    Console.WriteLine(result.Value);
}
else
{
    foreach (var error in result.Errors)
    {
        Console.WriteLine($"Error: {error.UserMessage}");
    }
}

๐Ÿ—๏ธ Core Concepts

Factory Methods

Method Purpose Use Case
Result<T>.Success(value) Create successful result Operation completed successfully
Result<T>.Failure(error) Create failed result Single error occurred
Result<T>.Failure(errors) Create failed result Multiple validation errors

Chaining Operations

Method Purpose When to Use
.Then(func) Chain operations that can fail Database calls, API calls, validation
.Transform(func) Transform values safely Formatting, calculations, property access

Properties

Property Type Purpose
IsSuccess bool Check if operation succeeded
Value T Get the result value (only if successful)
Errors IEnumerable<Error> Get all errors (empty if successful)

๐Ÿ“‹ Common Patterns

API Controller Pattern

[HttpPost]
public IActionResult CreateUser(CreateUserRequest request)
{
    var result = ValidateRequest(request)           // Validate input
        .Then(CreateUserInDatabase)                 // Database operation  
        .Transform(FormatResponse);                 // Format response

    return result.IsSuccess 
        ? Ok(result.Value)
        : BadRequest(result.Errors.Select(e => e.UserMessage));
}

private Result<CreateUserRequest> ValidateRequest(CreateUserRequest request)
{
    var errors = new List<Error>();
    
    if (string.IsNullOrWhiteSpace(request.Name))
        errors.Add(new Error(ErrorCode.ParameterMissing, "Name is required"));
        
    if (request.Age < 0)
        errors.Add(new Error(ErrorCode.ParameterOutOfRange, "Age must be positive"));
        
    return errors.Any() 
        ? Result<CreateUserRequest>.Failure(errors)
        : Result<CreateUserRequest>.Success(request);
}

Data Processing Pipeline

public Result<ProcessedData> ProcessFile(string filePath)
{
    return ReadFile(filePath)                       // Can fail - file I/O
        .Transform(content => content.Trim())       // Safe - string operation
        .Then(ParseJson)                           // Can fail - JSON parsing
        .Transform(data => data.Records)           // Safe - property access
        .Then(ValidateRecords)                     // Can fail - validation
        .Transform(FormatForOutput);               // Safe - formatting
}

Service Layer with Error Propagation

public async Task<Result<User>> GetUserWithPermissionsAsync(int userId, string requesterRole)
{
    return await GetUserById(userId)                    // Database call
        .Then(user => CheckPermissions(user, requesterRole))  // Authorization
        .Transform(user => HidePrivateFields(user))     // Data filtering
        .Transform(user => AddComputedFields(user));    // Enhancement
}

๐ŸŽจ Error Handling

Error Codes

The library provides comprehensive error codes for different scenarios:

// Client errors (4xx equivalent)
ErrorCode.BadRequest
ErrorCode.Unauthorized  
ErrorCode.Forbidden
ErrorCode.ResourceNotFound
ErrorCode.ParameterMissing
ErrorCode.ParameterOutOfRange
ErrorCode.BusinessRuleViolation

// Server errors (5xx equivalent)  
ErrorCode.ServerError
ErrorCode.DatabaseFailure
ErrorCode.ServiceUnavailable
ErrorCode.InfrastructureFailure

Creating Detailed Errors

// Basic error
var error = new Error(ErrorCode.BadRequest, "Invalid input");

// Error with technical details
var detailedError = new Error(
    ErrorCode.DatabaseFailure, 
    "Unable to save user",           // User message
    "Connection timeout after 30s"   // Technical message for logging
);

๐Ÿ”ง Advanced Usage

Custom Validation Functions

public static Result<User> ValidateUser(User user)
{
    var errors = new List<Error>();

    if (user.Age < 18)
        errors.Add(new Error(ErrorCode.BusinessRuleViolation, "User must be 18 or older"));
        
    if (user.Email?.Contains("@") != true)
        errors.Add(new Error(ErrorCode.BadRequest, "Invalid email format"));

    return errors.Any() 
        ? Result<User>.Failure(errors)
        : Result<User>.Success(user);
}

Conditional Operations

var result = GetUser(id)
    .Then(user => user.IsActive 
        ? Result<User>.Success(user)
        : Result<User>.Failure(new Error(ErrorCode.BusinessRuleViolation, "User is inactive")))
    .Transform(user => user.Name);

Type Transformations

// Transform between different types safely
Result<int> numberResult = Result<int>.Success(42);

Result<string> stringResult = numberResult
    .Transform(n => n.ToString());           // int -> string
    
Result<bool> boolResult = stringResult  
    .Transform(s => s.Length > 0);          // string -> bool

๐Ÿงช Testing

The library includes comprehensive tests demonstrating all usage patterns. Run tests with:

dotnet test

View the test file for detailed usage examples: ResultTests.cs

๐Ÿค Why Use Result Pattern?

Before (Exception-based)

public User GetUser(int id)
{
    try 
    {
        var user = database.GetUser(id);
        if (user == null) throw new NotFoundException("User not found");
        
        ValidateUser(user); // Throws on validation failure
        
        return ProcessUser(user); // Throws on processing failure
    }
    catch (Exception ex)
    {
        // Lost context, hard to handle multiple errors
        throw;
    }
}

After (Result pattern)

public Result<User> GetUser(int id)
{
    return database.GetUser(id)                     // Returns Result<User>
        .Then(ValidateUser)                         // Chain validation
        .Then(ProcessUser);                         // Chain processing
    
    // Errors are values, not exceptions
    // Multiple errors preserved
    // Type-safe, explicit error handling
}

Benefits

  • โœ… Explicit: Errors are part of the method signature
  • โœ… Composable: Chain operations elegantly
  • โœ… Safe: No null reference exceptions
  • โœ… Informative: Rich error context with codes and messages
  • โœ… Testable: Errors are values, easy to test
  • โœ… Performant: No exception throwing overhead

๐Ÿ“„ License

Dual License Model:

  • โœ… Free for personal, educational, and non-commercial use
  • ๐Ÿ’ผ Commercial license required for business/commercial use

See LICENSE.md for full details.

For commercial licensing inquiries, please contact the author.

๐Ÿ™‹ Contributing

Contributions welcome! Please read our contributing guidelines and submit pull requests to the main branch.

๐Ÿ“ž Support

  • Documentation: View tests for comprehensive examples
  • Issues: Report bugs or request features via GitHub Issues
  • Discussions: Ask questions in GitHub Discussions
Product Compatible and additional computed target framework versions.
.NET net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 is compatible.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 was computed.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.
  • net8.0

    • No dependencies.
  • net9.0

    • No dependencies.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
0.9.0 66 8/22/2025

v0.9.0 - Release Candidate:
     - Production-ready Result<T> pattern with comprehensive error handling
     - Railway pattern operations: Then() for chainable error propagation
     - Safe transformations: Transform() for value conversions without exceptions
     - Rich Error system with structured ErrorCodes (400-500 series) and dual messages
     - Multiple error accumulation for complex validation scenarios
     - Real-world e-commerce examples: User โ†’ Order โ†’ Payment โ†’ Email workflows
     - 100 comprehensive tests covering success, failure, chaining, and error recovery
     - Zero dependencies, multi-target support (.NET 8, 9)
     - Battle-tested patterns from production applications