YellowCucumber.ErrorHandling.Analyzers 1.0.0

dotnet add package YellowCucumber.ErrorHandling.Analyzers --version 1.0.0
                    
NuGet\Install-Package YellowCucumber.ErrorHandling.Analyzers -Version 1.0.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="YellowCucumber.ErrorHandling.Analyzers" Version="1.0.0">
  <PrivateAssets>all</PrivateAssets>
  <IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
</PackageReference>
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="YellowCucumber.ErrorHandling.Analyzers" Version="1.0.0" />
                    
Directory.Packages.props
<PackageReference Include="YellowCucumber.ErrorHandling.Analyzers">
  <PrivateAssets>all</PrivateAssets>
  <IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
</PackageReference>
                    
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 YellowCucumber.ErrorHandling.Analyzers --version 1.0.0
                    
#r "nuget: YellowCucumber.ErrorHandling.Analyzers, 1.0.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 YellowCucumber.ErrorHandling.Analyzers@1.0.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=YellowCucumber.ErrorHandling.Analyzers&version=1.0.0
                    
Install as a Cake Addin
#tool nuget:?package=YellowCucumber.ErrorHandling.Analyzers&version=1.0.0
                    
Install as a Cake Tool

YellowCucumber.ErrorHandling

A modern .NET library for handling operation results and execution statuses in a type-safe manner. This library provides a comprehensive result pattern implementation that eliminates exceptions in business logic and promotes clear error handling patterns.

Features

  • Type-safe Result Pattern: Strongly-typed result handling with clear success/failure semantics
  • Execution Status System: Extensible status types using modern C# features (records, CRTP pattern)
  • Message Propagation: Detailed error messages and context information
  • Pattern Matching: Fluent API for handling different result states
  • Async Support: Full async/await integration
  • Roslyn Analyzers: Compile-time safety checks for proper usage patterns
  • .NET 9 Ready: Built with the latest .NET features and performance optimizations

Quick Start

Installation

dotnet add package YellowCucumber.ErrorHandling

Basic Usage

using Results.Core;

// Simple result without a value
Result result = Result.Success();
Result failedResult = Result.Failure();

// Result with a value
Result<string> dataResult = Result<string>.Success("Hello, World!");
Result<int> errorResult = Result<int>.Failure();

// Check the status
if (result.Status is Success)
{
    Console.WriteLine("Operation succeeded!");
}

// Get value safely
string value = dataResult.TryGetValue("default");

Detailed Results with Messages

using Results.Core;

// Create detailed results with contextual messages
DetailedResult<User> userResult = await ValidateUserAsync(userData);

if (userResult.Status is Failure)
{
    Console.WriteLine($"Validation failed: {userResult.Message.Message}");
    if (userResult.Message.Exception != null)
    {
        // Handle the underlying exception
        logger.LogError(userResult.Message.Exception, "User validation error");
    }
}
else
{
    User user = userResult.TryGetValue(null);
    // Process the valid user
}

Pattern Matching

string result = userResult.Match<string>(builder => builder
    .When<Success>(value => $"User created: {value.Name}")
    .When<Warning>(value => $"User created with warnings: {value.Name}")
    .When<Failure>(() => "User creation failed")
    .Otherwise(() => "Unknown status"));

// Async pattern matching
await userResult.MatchAsync(builder => builder
    .When<Success>(async user => await SendWelcomeEmailAsync(user))
    .When<Failure>(async () => await LogFailureAsync())
    .Otherwise(async () => await HandleUnknownStatusAsync()));

Custom Status Types

// Define custom execution statuses
public record PendingStatus : ExecutionStatus<PendingStatus>
{
    public PendingStatus() : base("Pending") { }
}

public record ProcessingStatus : ExecutionStatus<ProcessingStatus>
{
    public ProcessingStatus() : base("Processing") { }
}

// Use them in results
Result<string> pendingResult = Result<string>.Create<PendingStatus>("Queued for processing");

Custom Messages

public record ValidationMessage : ExecutionMessage<ValidationMessage>
{
    public ValidationMessage(string field, string issue) 
        : base($"Validation error in {field}: {issue}") { }
    
    public ValidationMessage(string field, string issue, Exception exception) 
        : base($"Validation error in {field}: {issue}", exception) { }
}

// Use with DetailedResult
DetailedResult<User> result = DetailedResult<User>.Failure<ValidationMessage>();

Chaining Operations

public class UserService
{
    public DetailedResult<User> CreateUser(CreateUserRequest request)
    {
        var validationResult = ValidateRequest(request);
        if (validationResult.Status is Failure)
        {
            return DetailedResult<User>.Failure(validationResult.Message);
        }
        
        var user = new User { Name = request.Name, Email = request.Email };
        var saveResult = SaveUser(user);
        
        return saveResult.Status switch
        {
            Success => DetailedResult<User>.Create<Success>(user, saveResult.Message),
            _ => DetailedResult<User>.Failure(saveResult.Message)
        };
    }
    
    private DetailedResult ValidateRequest(CreateUserRequest request)
    {
        if (string.IsNullOrEmpty(request.Name))
        {
            var message = new ValidationMessage("Name", "Name is required");
            return DetailedResult.Failure(message);
        }
        
        return DetailedResult.Create<Success, SuccessMessage>();
    }
}

API Reference

Core Types

  • Result<T>: Result with a value and execution status
  • Result: Result without a value (payload-less operations)
  • DetailedResult<T>: Result with value, status, and detailed message
  • DetailedResult: Payload-less result with detailed message

Status Types

  • Success: Operation completed successfully
  • Warning: Operation completed with warnings
  • Failure: Operation failed
  • ExecutionStatus<TSelf>: Base class for custom statuses

Message Types

  • IExecutionMessage: Interface for execution messages
  • ExecutionMessage<TSelf>: Base class for custom messages

Pattern Matching

  • MatchBuilder<TResult>: Synchronous pattern matching with return value
  • MatchBuilder: Synchronous pattern matching without return value
  • AsyncMatchBuilder<TResult>: Asynchronous pattern matching with return value
  • AsyncMatchBuilder: Asynchronous pattern matching without return value

Best Practices

1. Use Appropriate Result Types

// For operations that return data
Result<User> GetUser(int id);

// For operations that don't return data
Result DeleteUser(int id);

// When you need detailed error information
DetailedResult<User> ValidateAndCreateUser(CreateUserRequest request);

2. Handle All Status Cases

// Use pattern matching to handle all cases
var message = result.Match<string>(builder => builder
    .When<Success>(user => $"Welcome, {user.Name}!")
    .When<Warning>(user => $"Welcome, {user.Name}! Please verify your email.")
    .When<Failure>(() => "Registration failed. Please try again.")
    .Otherwise(() => "Unknown status"));

3. Create Domain-Specific Statuses

public record ValidationFailure : ExecutionStatus<ValidationFailure>
{
    public ValidationFailure() : base("ValidationFailure") { }
}

public record NetworkTimeout : ExecutionStatus<NetworkTimeout>
{
    public NetworkTimeout() : base("NetworkTimeout") { }
}

4. Use Custom Messages for Context

public record DatabaseErrorMessage : ExecutionMessage<DatabaseErrorMessage>
{
    public DatabaseErrorMessage(string operation, Exception exception)
        : base($"Database operation '{operation}' failed", exception) { }
}

Error Handling Without Exceptions

This library promotes handling errors as data rather than exceptional control flow:

// Instead of this:
public User GetUser(int id)
{
    if (id <= 0)
        throw new ArgumentException("Invalid user ID");
    
    var user = database.GetUser(id);
    if (user == null)
        throw new UserNotFoundException($"User {id} not found");
    
    return user;
}

// Use this:
public DetailedResult<User> GetUser(int id)
{
    if (id <= 0)
    {
        var message = new ValidationMessage("id", "User ID must be positive");
        return DetailedResult<User>.Failure(message);
    }
    
    var user = database.GetUser(id);
    if (user == null)
    {
        var message = new NotFoundMessage($"User {id} not found");
        return DetailedResult<User>.Failure(message);
    }
    
    var successMessage = new SuccessMessage($"User {id} retrieved successfully");
    return DetailedResult<User>.Create<Success>(user, successMessage);
}

Async Support

Full support for async operations:

public async Task<DetailedResult<User>> CreateUserAsync(CreateUserRequest request)
{
    var validationResult = await ValidateUserAsync(request);
    if (validationResult.Status is Failure)
    {
        return DetailedResult<User>.Failure(validationResult.Message);
    }
    
    var user = new User(request.Name, request.Email);
    var saveResult = await SaveUserAsync(user);
    
    return saveResult.Match(builder => builder
        .When<Success>(() => DetailedResult<User>.Create<Success>(user, saveResult.Message))
        .Otherwise(() => DetailedResult<User>.Failure(saveResult.Message)));
}

Roslyn Analyzers

The library includes Roslyn analyzers to enforce proper usage patterns at compile time:

  • RequireOtherwiseAnalyzer: Ensures that pattern matching operations include an Otherwise clause for completeness

Contributing

Contributions are welcome! Please read our contributing guidelines and submit pull requests to our GitHub repository.

License

This project is licensed under the MIT License - see the LICENSE file for details.

Changelog

Version 1.0.0

  • Initial release
  • Core Result and DetailedResult types
  • Pattern matching support
  • Async/await integration
  • Roslyn analyzers
  • .NET 9 support
There are no supported framework assets in this package.

Learn more about Target Frameworks and .NET Standard.

NuGet packages (1)

Showing the top 1 NuGet packages that depend on YellowCucumber.ErrorHandling.Analyzers:

Package Downloads
YellowCucumber.ErrorHandling.CodeFixes

Code fixes for YellowCucumber.ErrorHandling analyzers to provide automatic corrections for common usage patterns. Requires .NET 8.0 or later.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.0.0 454 11/6/2025

Initial release with RequireOtherwiseAnalyzer to ensure pattern matching operations include Otherwise clause.