Toarnbeike.Results 1.0.0

dotnet add package Toarnbeike.Results --version 1.0.0
                    
NuGet\Install-Package Toarnbeike.Results -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="Toarnbeike.Results" Version="1.0.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Toarnbeike.Results" Version="1.0.0" />
                    
Directory.Packages.props
<PackageReference Include="Toarnbeike.Results" />
                    
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 Toarnbeike.Results --version 1.0.0
                    
#r "nuget: Toarnbeike.Results, 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 Toarnbeike.Results@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=Toarnbeike.Results&version=1.0.0
                    
Install as a Cake Addin
#tool nuget:?package=Toarnbeike.Results&version=1.0.0
                    
Install as a Cake Tool

CI Code Coverage .NET 9 License: MIT

Toarnbeike.Results

Toarnbeike.Results is a lightweight, expressive C# implementation of the Result monad. It enhances code clarity and reliability by enabling clear, exception-free handling of success, failure, and validation outcomes.


Features

  • Explicit error handling: Avoid exceptions for control flow; return meaningful failures instead.
  • Fluent extension methods: Compose operations with Bind, Map, Match, Tap, and more.
  • Strongly typed generic results: Preserve either a success value or failure details in one type-safe container.
  • Rich validation support: Aggregate validation failures with rich property-level details.
  • Seamless integration: All methods support asynchronous pipelines.
  • Functional programming inspired: Inspired by FP principles for predictable, readable, and maintainable error handling.

Core concepts

  • Result: Represents a successful or failed operation.
  • Result<TValue>: A Result carrying a value on success.
  • Failure: Encapsulates error details, including validation failures.
  • ValidationFailure: Describes a property-level validation issue.
  • AggregateFailure: Represents grouped validation failures.

Packages

Package Description NuGet
Toarnbeike.Results Core option type, extension methods and collections NuGet
Toarnbeike.Results.FluentValidation Validation integration using FluentValidation NuGet
Toarnbeike.Results.Messaging CQRS Request and notification messaging NuGet
Toarnbeike.Results.MinimalApi Integration with Microsoft.AspNetCore minimal API's NuGet
Toarnbeike.Results.Optional Integration with Toarnbeike.Optional NuGet

Inspiration

This project draws inspiration from:


Getting Started

dotnet add package Toarnbeike.Results

Then import on or more of the following namespaces:

using Toarnbeike.Results;			// Base namespace for Result, Result<TValue> and Failure
using Toarnbeike.Results.Collections;		// For extensions on IEnumerable<Result<T>>
using Toarnbeike.Results.Extensions;		// For functional extensions on Result and Result<TValue>
using Toarnbeike.Results.Failure;		// For additional failure types
using Toarnbeike.Results.Linq;		        // For LINQ query syntax support
using Toarnbeike.Results.TestHelpers;           // For fluent assertions on results. 

Basic Usage

A result can represent either success (with or without a value) or failure (with error details):

// Success Results
var result1 = Result.Success();						
var result2 = Result<int>.Success(42);					
var result3 = Result.Success(42);					// Type inferred
Result<int> result4 = 42;						// Implicit conversion

// Failure Results
var result4 = Result<int>.Failure(new Failure("Code", "Message"));	
Result<int> result5 = new Failure("Code", "Message");			// implicit conversion

Extracting the value or the failure from the rsult:

if (result1.TryGetValue(out var value, out var failure)) 
{
	Console.WriteLine(value);					// 42
}

Extension Methods

The Toarnbeike.Results.Extensions namespace includes rich extensions for Result and Result<T>:

Method Result Result<T> Description
Bind(...) Chains operations returning Result<TOut>
Check(...) Check a condition on the success value, or returns a failure
Map(...) Maps the success value to another type
Match(...) Converts to another type using success/failure lambdas
Tap(...) Executes side-effects on success
TapAlways(...) Executes side-effects on any result
TapFailure(...) Executes side-effects on failure
Verify(...) Verifies another result; propagates failure if needed
VerifyWhen(...) Conditionally verifies another result
WithValue(...) Adds a value to a non-generic result
Zip(...) Combines two results into a Result<(T1,T2)>

All methods support async variants and operate seamlessly with Task<Result<T>>.

For information per method see the Extensions README.


Collections

The Toarnbeike.Results.Collections namespace provides extension methods for working with collections of results:

Method ReturnType Description
AllSuccess() bool Returns true if all results in the collection are successful
Sequence() Result<IEnumerable<T>> Returns all success values or the first encountered failure
Aggregate() Result<IEnumerable<T>> Returns all success values or an AggregateFailure containing all failures
SuccessValues() IEnumerable<T> Extracts all success values from a collection of results
Failures() IEnumerable<Failure> Extracts all failures from a collection of results
Split() (IEnumerable<T>, IEnumerable<Failure>) Splits the collection into success values and failures

All methods support async variants and operate seamlessly with IEnumerable<Task<Result<T>>>.


Failures

The Toarnbeike.Results.Failures namespace include a couple of default failures:

AggregateFailure

Represents a collection of multiple failures, typically used when working with collections of Result<T>:

var results = new List<Result<int>>
{
    Result<int>.Success(1),
    Result<int>.Failure(new Failure("Code1", "Message1")),
    Result<int>.Failure(new Failure("Code2", "Message2"))
};

var aggregateResult = results.Aggregate();      // Result<IEnumerable<int>>
var ggregateFailure = aggregateResult
                        .ShouldBeFailureOfType<AggregateFailure>();

ExceptionFailure

Used when converting exceptions to failures via the Try factory:

var result1 = Result.Try(() => int.Parse("123"));	// Success(123);
var result2 = Result.Try(() => int.Parse("abc"))	// Failure(ExceptionFailure);

ValidationFailure

Represents a property-level validation issue:

new ValidationFailure("Email", "Email is required.");

ValidationFailures

Aggregates multiple ValidationFailure instances. Typically produced using the FluentValidation integration.


LINQ Query syntax support

Toarnbeike.Results supports optional integration with C# LINQ query syntax, making it easier to compose multiple Result<T> computations in a declarative style.

Why use LINQ Query Syntax?

While method chaining works well for most scenarios, C#�s LINQ query syntax can make some workflows more expressive and readable � especially when you want to:

  • Name intermediate results using let
  • Compose complex Result pipelines in a declarative way
  • Avoid deeply nested lambdas in Bind and Map

Example:

using Toarnbeike.Results.Linq;

var result =
    from id in GetUserId()
    from user in GetUserById(id)
    let fullName = $"{user.FirstName} {user.LastName}"
    select new UserDto(fullName, user.Email);

When comparing that with method chaining, the LINQ query syntax can be more readable, especially for complex workflows:

var result = GetUserId()
    .Bind(GetUserById)
    .Map(user =>
    {
        var name = $"{user.FirstName} {user.LastName}";
        return new UserDto(name, user.Email);
    });

Which also works, but makes name only available inside the Map lambda.


TestHelpers

These are ideal for unit testing and compatible with any test framework. See the TestHelpers README for details.


Why Results?

Exceptions should be exceptional. Result<T> gives you clarity, safety, and composability � without relying on exceptions for control flow.

Product Compatible and additional computed target framework versions.
.NET 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.
  • net9.0

    • No dependencies.

NuGet packages (3)

Showing the top 3 NuGet packages that depend on Toarnbeike.Results:

Package Downloads
Toarnbeike.Results.FluentValidation

Integrating FluentValidation with Toarnbeike.Results

Toarnbeike.Results.Optional

Convert between Toarnbeike.Results an Toarnbeike.Optional

Toarnbeike.Results.MinimalApi

Return Toarnbeike.Results from your AspNetCore Minimal Api Endpoints

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.0.0 244 8/28/2025