Knight.Response 1.1.0

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

Knight.Response

Knight.Response is a lightweight, immutable, fluent result library for C# services, APIs, and applications. It provides a clean and consistent way to handle outcomes, success/failure states, messages, and functional chaining — making your code simpler, safer, and more expressive.

NuGet Version ci Mutation score


Features

  • Immutable Result and Result<T> types
  • Statuses: Completed, Cancelled, Failed, Error
  • Rich Message with MessageType + optional structured Metadata
  • Factory methods: Success, Failure, Error, Cancel, NotFound, FromCondition, Aggregate, Error(Exception)
  • Functional extensions: OnSuccess, OnFailure, Map, Bind
  • Advanced extensions: Ensure, Tap, Recover, WithMessage, WithMessages
  • Pattern matching via deconstruction
  • Zero runtime dependencies

Installation

dotnet add package Knight.Response

Quick Start

using Knight.Response;

var userResult = Results.Success(new User("Knight"))
    .Ensure(u => u!.IsActive, "User is not active")
    .Tap(u => _audit.LogLogin(u!));

if (userResult.IsSuccess)
{
    Console.WriteLine(userResult.Value!.Name);
}

Core Concepts

public class Result
{
    public Status Status { get; }
    public IReadOnlyList<Message> Messages { get; }
    public bool IsSuccess { get; } // Status == Completed
}

public class Result<T> : Result
{
    public T? Value { get; }
}

Status

  • Completed
  • Cancelled
  • Failed
  • Error

MessageType

  • Information
  • Warning
  • Error

Metadata

Message supports optional Metadata as a read-only dictionary for structured context, e.g.:

var msg = new Message(
    MessageType.Warning,
    "Rate limit exceeded",
    new Dictionary<string, object?> { ["retryAfter"] = 30 }
);

Factory Methods

Method Description
Results.Success() Create a success result
Results.Success<T>(T value) Success result with data
Results.Success<T>(T value, IReadOnlyList<Message> messages) Success with value and messages
Results.Failure(string reason) Failure result with message
Results.Failure(IReadOnlyList<Message> messages) Failure from messages
Results.Error(string reason) Error result
Results.Error(Exception ex) Error result from exception
Results.Cancel(string reason) Cancelled result (defaults to Warning)
Results.Cancel(IReadOnlyList<Message> messages) Cancelled from messages
Results.NotFound() "Not found" (defaults to Completed + Warning)
Results.NotFound(string message, Status status = Status.Completed) "Not found" with override status
Results.FromCondition(bool condition, string failMessage) Success if true, Failure if false
Results.Aggregate(IEnumerable<Result> results) Combine multiple results

Core Extensions

result.OnSuccess(() => Console.WriteLine("All good"))
      .OnFailure(msgs => Console.WriteLine($"Failed: {string.Join(", ", msgs.Select(m => m.Content))}"));

var mapped = Results.Success(2).Map(x => x * 5); // Success(10)

var bound = Results.Success("abc").Bind(v => Results.Success(v.ToUpper()));
  • OnSuccess – Runs an action if result is success
  • OnFailure – Runs an action if result is failure/error/cancelled
  • Map – Transforms the value if success
  • Bind – Chains another Result operation if success

Advanced Extensions

  • Ensure – Ensures a predicate holds for success; otherwise returns failure
  • Tap – Executes an action for side effects, preserving the result
  • Recover – Transforms a failure into a success with fallback value
  • WithMessage – Adds a single message to a result
  • WithMessages – Adds multiple messages to a result
var ensured = Results.Success(5).Ensure(x => x > 0, "Must be positive");
var tapped = ensured.Tap(v => Console.WriteLine($"Value: {v}"));
var recovered = Results.Failure<int>("bad").Recover(_ => 42);
var withMsg = recovered.WithMessage(new Message(MessageType.Information, "Recovered with default"));

Pattern Matching

You can deconstruct results directly:

var (status, messages) = Results.Failure("fail!");

var (status, messages, value) = Results.Success(42);

Or use C# pattern matching:

switch (result)
{
    case { Status: Status.Completed }:
        Console.WriteLine("Success!");
        break;
    case { Status: Status.Failed, Messages: var msgs }:
        Console.WriteLine($"Failed: {msgs[0].Content}");
        break;
}

Important: Result<T>.Value and default values

When T is a non-nullable value type, failed results will contain the default value of that type (0 for int, false for bool, etc.). This is a limitation of .NET generics, since value types always have a default.

var r1 = Results.Failure<int>("bad");
Console.WriteLine(r1.Value); // 0

var r2 = Results.Failure<int?>("bad");
Console.WriteLine(r2.Value); // null

Guidance

  • Use nullable value types (e.g. int?, bool?) if you want null to represent "no value".
  • Use non-nullable value types if a default like 0 or false makes sense in your domain.
  • Reference types (e.g. string, User) will correctly return null when not Completed.

Status values

The Status enum indicates the outcome of an operation:

  • Completed – success
  • Cancelled – cancelled before completion
  • Failed – failed due to a known issue
  • Error – unexpected/unhandled error

Validation Support (new in 1.1.0)

You can now construct Result and Result<T> from System.ComponentModel.DataAnnotations.ValidationResult collections.

using System.ComponentModel.DataAnnotations;

var errors = new List<ValidationResult>
{
    new("Name is required", new[] { "Name" }),
    new("Amount must be greater than 0", new[] { "Amount" }),
    new("General rule failed")
};

// Produces Error result with prefixed messages:
// "Name: Name is required", "Amount: Amount must be greater than 0", "General rule failed"
var result = Results.Validation(errors);

For generics:

var result = Results.Validation<MyEntity>(errors);

Metadata Enricher Overloads

Use an enricher delegate to attach metadata (e.g., field name) without altering text:

var result = Results.Validation(
    errors,
    (msg, field) => string.IsNullOrEmpty(field)
        ? msg
        : msg.WithMeta("field", field) // your custom extension
);

Default behavior:

  • Prefixed mapping: "Field: message"
  • Enricher overload: raw message text, field passed separately

License

This project is licensed under the MIT License.

Contributing

Contributions are welcome! Please read CONTRIBUTING.md.

Product Compatible and additional computed target framework versions.
.NET net5.0 was computed.  net5.0-windows was computed.  net6.0 was computed.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net7.0 was computed.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net8.0 was computed.  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 was computed.  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. 
.NET Core netcoreapp2.0 was computed.  netcoreapp2.1 was computed.  netcoreapp2.2 was computed.  netcoreapp3.0 was computed.  netcoreapp3.1 was computed. 
.NET Standard netstandard2.0 is compatible.  netstandard2.1 was computed. 
.NET Framework net461 was computed.  net462 was computed.  net463 was computed.  net47 was computed.  net471 was computed.  net472 was computed.  net48 was computed.  net481 was computed. 
MonoAndroid monoandroid was computed. 
MonoMac monomac was computed. 
MonoTouch monotouch was computed. 
Tizen tizen40 was computed.  tizen60 was computed. 
Xamarin.iOS xamarinios was computed. 
Xamarin.Mac xamarinmac was computed. 
Xamarin.TVOS xamarintvos was computed. 
Xamarin.WatchOS xamarinwatchos was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages (1)

Showing the top 1 NuGet packages that depend on Knight.Response:

Package Downloads
Knight.Response.Abstractions.Http

Framework-agnostic HTTP abstractions for Knight.Response (shared options and validation mapping).

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.1.0 181 9/1/2025
1.0.2 338 8/26/2025
1.0.1 242 8/15/2025
1.0.0 132 8/14/2025

Added:
           - Support for creating Results from **ValidationResult** collections:
           • `Results.Validation(...)` and `Results.Validation{T}(...)`
           • Converts validation errors into error messages with field prefixes ("Field: message").
           - Enricher overloads for attaching metadata:
           • `Results.Validation(errors, (msg, field) => ...)`
           • Enables adding structured metadata (e.g., field names) without altering message text.

           Changed:
           - Normalized validation messages:
           • Trimmed whitespace from content and field names.
           • Fallback to "Validation error." when message is null/empty/whitespace.
           - Minor internal refactors for robustness and testability.

           Fixed:
           - Ensured `Result` constructors normalize `null` message lists to empty collections.
           - Achieved **100% mutation score** with Stryker.