JoakimAnder.Toolbox 0.3.1

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

JoakimAnder.Toolbox

A collection of helper classes and utilities I've grown tired of copy-pasting between projects.

NuGet CI License: MIT

Installation

dotnet add package JoakimAnder.Toolbox

Requires .NET 10 or later.

What's in the box

ParallelFanout — JoakimAnder.Toolbox.Threading.FanOut

Run several async operations that must all succeed, and cancel the rest the instant one fails — so a long-running sibling isn't left burning time and resources after the outcome is already decided.

using JoakimAnder.Toolbox.Threading;

var (user, orders) = await new FanOut()
    .Add(ct => GetUserAsync(ct))
    .Add(ct => GetOrdersAsync(ct))
    .WhenAll(cancellationToken);

If GetUserAsync throws, the token handed to GetOrdersAsync is cancelled and the original exception is rethrown unwrapped. Operations are factories (ct => …) so the token can be threaded into each one. A static FanOut.WhenAll(op1, op2, …) is also available for quick one-liners — both typed (returning a tuple) and void, for arities 2–8, plus an IEnumerable<Func<CancellationToken, Task>> overload for dynamic counts.

Result — JoakimAnder.Toolbox.Results.Result<T, TError> / Result<TError>

A typed success/failure container for the boundaries of your code. Producers return values via implicit conversions; consumers read via Match or TryGetValue — never via loose Value / Error properties.

using JoakimAnder.Toolbox.Results;

public Task<Result<UserSummary, ApiError>> GetUserSummaryAsync(int id, CancellationToken ct) =>
    Result.TryAsync(
        async c => new UserSummary(await _users.GetAsync(id, c), await _orders.CountAsync(id, c)),
        ex => ex switch
        {
            KeyNotFoundException k  => (ApiError)new ApiError.NotFound(k.Message),
            HttpRequestException h  => new ApiError.Upstream(nameof(HttpRequestException), h.Message),
            _                       => new ApiError.Unexpected(ex.Message)
        },
        ct);

The caller defines ApiError (typically a sealed-record discriminated union) so the boundary handler can switch on it exhaustively at compile time. Map / MapError / Bind compose Result-returning calls — sync and async — and Result.Try / ValueOrThrow provide explicit interop with throwing code (OperationCanceledException is always rethrown so cancellation stays cancellation).

A void-success variant Result<TError> mirrors the typed one for commands that succeed without a payload.

See docs/ROADMAP.md for the project roadmap.

DI registration — attributes + source generator

Mark a class with [Singleton], [Scoped], or [Transient] and the generator emits the IServiceCollection registration for you — no reflection, no hand-maintained AddX lists.

using JoakimAnder.Toolbox.DependencyInjection;

[Singleton(typeof(IClock))] class SystemClock : IClock { }
[Scoped] class OrderService { }

// in Program.cs:
builder.Services.AddAttributedServices();

Omit the service type to register the concrete type itself. Use Group = "..." to emit a separate AddAttributed<Group>Services() method, and Key = "..." for keyed services. The attributes are generated into your assembly, so there is no extra runtime dependency.

Web API example — examples/JoakimAnder.Toolbox.Examples.WebApi

A small ASP.NET Core Minimal-API project showing all three Toolbox features composed in a realistic shape — vertical-slice handlers that return Result<T, ApiError>, a [Scoped]/[Singleton] service graph wired by the DI source generator, and a fan-out endpoint (GET /books/{id}/detail) that fetches author + reviews in parallel via Result.TryAsync(new FanOut().Add(…)).

dotnet run --project examples/JoakimAnder.Toolbox.Examples.WebApi

Use examples/JoakimAnder.Toolbox.Examples.WebApi/WebApi.http (REST Client, Rider, or VS 2022+ run these in-place) or the auto-generated OpenAPI document at /openapi/v1.json.

Project structure

  • src/JoakimAnder.Toolbox — the library
  • src/JoakimAnder.Toolbox.SourceGenerators — source generators, packed into the main NuGet
  • tests/ — xUnit test projects
  • examples/ — runnable usage examples

Building locally

dotnet build
dotnet test

Versioning

Versions are derived from git tags via MinVer. Tag a commit v1.2.3 to release 1.2.3 to NuGet.

License

MIT — see LICENSE.

AI usage

See AI_DISCLOSURE.md.

Product Compatible and additional computed target framework versions.
.NET net10.0 is compatible.  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.
  • net10.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.3.1 100 9/4/2026
0.3.0 111 5/31/2026
0.2.0 115 5/23/2026