Note This library targets
netstandard2.0andnetstandard2.1. It is written in F# and can be used from both C# and F#.
The goal of this repository is to give methods and processes one way to report their outcome. It is a structured response and callers don't need a different return shape for every operation.
Every operation returns the same structured, easy-to-parse model. The caller can check whether it succeeded, read the value it produced, and inspect the messages that explain a failure.
This is a deliberately minimal F# take on the author's C# library RzR.ResultMessage. It keeps only the core result model: no message types or action helpers, and a single fluent extension (WithExtension).
What the library offers:
MinimalResult<T>: the result of an operation that returns a value. Create it withSuccess(response),Failure(code, message)orFailure(messages). It exposesIsSuccess,IsFailure,Response(the default value ofTon a failure) andMessages.MinimalResult: the non-generic result for operations that return no value. It inherits fromMinimalResult<object>and offersSuccess(),Failure(code, message)andFailure(messages).ResultMessage: one message attached to a result. It has aCode, aMessage(may benull) andExtensions(additional data keyed by name, may benull).- Additional failure data: pass a dictionary to
Failure(code, message, extensions)(stored as a read-only copy), or add one entry at a time withWithExtension(key, value), which works on successes and failures. IMinimalResultandIMinimalResult<T>: interfaces for code that only needs to read a result.- Implicit conversions:
- from
Tto a successfulMinimalResult<T>. - from
booltoMinimalResult.falsegives a failure with codeUnSuccessfullyReqExec. - from
Exceptionto a failure with codeUnhandledExceptionand the exception'sMessage. Anullexception throwsArgumentNullException.
- from
| Namespace | Types |
|---|---|
RzR.MinimalResultMessage |
MinimalResult<T>, MinimalResult |
RzR.MinimalResultMessage.Models |
ResultMessage |
RzR.MinimalResultMessage.Abstractions |
IMinimalResult, IMinimalResult<T> |
Results and messages use reference equality. Two results with the same content are not equal.
The only dependency is FSharp.Core (9.0.100 or newer). Add the library to your project and you can start using it.
dotnet add package RzR.MinimalResultMessage
or, from the Package Manager Console:
Install-Package RzR.MinimalResultMessage
using System;
using RzR.MinimalResultMessage;
public static class OrderService
{
public static MinimalResult<int> ParseQuantity(string input)
{
if (int.TryParse(input, out var quantity))
return quantity; // implicit conversion: success
return MinimalResult<int>.Failure("InvalidQuantity", $"'{input}' is not a number.");
}
public static MinimalResult Save(int quantity)
{
try
{
// persist the quantity
return true; // implicit conversion: success
}
catch (Exception ex)
{
return ex; // implicit conversion: failure with code "UnhandledException"
}
}
public static void Print(MinimalResult<int> result)
{
if (result.IsSuccess)
Console.WriteLine(result.Response);
else
foreach (var m in result.Messages)
Console.WriteLine($"{m.Code}: {m.Message}");
}
}In F#, call the conversions explicitly with op_Implicit. A type-annotated binding such as let r : MinimalResult = false also works, but raises warning FS3391.
open System
open RzR.MinimalResultMessage
let parseQuantity (input: string) : MinimalResult<int> =
match Int32.TryParse input with
| true, quantity -> MinimalResult<int>.Success quantity
| _ -> MinimalResult<int>.Failure("InvalidQuantity", $"'{input}' is not a number.")
let save (quantity: int) : MinimalResult =
try
// persist the quantity
MinimalResult.op_Implicit true
with ex ->
MinimalResult.op_Implicit ex
let result = parseQuantity "12"
if result.IsSuccess then printfn $"{result.Response}"
else result.Messages |> Seq.iter (fun m -> printfn $"{m.Code}: {m.Message}")Run the commands from the solution folder:
cd src
dotnet test --solution RzR.Shared.ResultMessage.slnx
Note This requires the .NET 10 SDK. The
global.jsoninsrcselects Microsoft.Testing.Platform as the test runner, and that setting only applies when you rundotnet testfrom that folder.