MicroResult 1.0.0
See the version list below for details.
dotnet add package MicroResult --version 1.0.0
NuGet\Install-Package MicroResult -Version 1.0.0
<PackageReference Include="MicroResult" Version="1.0.0" />
<PackageVersion Include="MicroResult" Version="1.0.0" />
<PackageReference Include="MicroResult" />
paket add MicroResult --version 1.0.0
#r "nuget: MicroResult, 1.0.0"
#:package MicroResult@1.0.0
#addin nuget:?package=MicroResult&version=1.0.0
#tool nuget:?package=MicroResult&version=1.0.0
MicroResult
MicroResult is a tiny library for returning either a successful value or an error without throwing exceptions in normal control flow.
It is designed for modern .NET applications that want predictable flow, low overhead, and a minimal API surface.
Features
✅ Zero Dependencies — Just add and use
⚡ Zero Allocations — Readonly struct, stack-only
🚀 AOT Friendly — Native AOT compilation ready
🎯 Minimal API Surface — Only essential methods (Match, Map, Bind, Ensure)
🔗 Fluent Chaining — Composable, functional error handling
✨ Clean DX — Implicit conversions + Match patterns
Installation
dotnet add package MicroResult
What It Does
Instead of throwing exceptions for expected failures, MicroResult returns a value that is either:
- a success containing
T - a failure containing
Error
That makes business flow explicit and easy to compose.
public Result<User> GetUser(Guid id)
{
var user = db.Users.Find(id);
if (user is null)
return new Error("NotFound", "User not found");
return user;
}
You then handle both outcomes deliberately:
var result = GetUser(id);
return result.Match(
user => Results.Ok(user),
error => Results.BadRequest(error));
Quick Start
Basic Usage
using MicroResult;
// Define errors
public static class Errors
{
public static readonly Error NotFound = new("NotFound", "User not found");
public static readonly Error Invalid = new("Invalid", "Invalid user");
}
// Return Result<T>
public Result<User> GetUser(Guid id)
{
var user = db.Users.Find(id);
if (user == null)
return Errors.NotFound; // Implicit conversion!
return user; // Implicit conversion!
}
// Consume with Match
var result = GetUser(id);
return result.Match(
onSuccess: user => Ok(user),
onFailure: error => BadRequest(error)
);
Chaining with Bind & Map
var result = GetUser(id)
.Bind(ValidateUser)
.Map(user => new UserDto(user));
return result.Match(
user => Ok(user),
error => BadRequest(error)
);
Why Chaining Helps
var result = GetUser(id)
.Bind(ValidateUser)
.Map(user => new UserDto(user));
Each step only runs when the previous step succeeded. If any step returns an error, the chain stops and preserves that failure.
Core API
Error
public readonly struct Error
{
public string Code { get; }
public string Message { get; }
public Error(string code, string message);
public override string ToString(); // "Code: Message"
}
Result<T>
public readonly struct Result<T>
{
public bool IsSuccess { get; }
public bool IsFailure { get; }
public T Value { get; } // Throws if failure
public Error Error { get; } // Throws if success
// Factory methods
public static Result<T> Success(T value);
public static Result<T> Failure(Error error);
// Core methods
public TResult Match<TResult>(Func<T, TResult> onSuccess, Func<Error, TResult> onFailure);
public Result<TResult> Map<TResult>(Func<T, TResult> func);
public Result<TResult> Bind<TResult>(Func<T, Result<TResult>> func);
public Result<T> Ensure(Func<T, bool> predicate, Error error);
// Implicit conversions
public static implicit operator Result<T>(T value);
public static implicit operator Result<T>(Error error);
}
Extensions
// Side effects
public static Result<T> Tap<T>(this Result<T> result, Action<T> action);
public static Result<T> OnFailure<T>(this Result<T> result, Action<Error> action);
Real-World Use Cases
// Validation
return age >= 18
? user
: new Error("InvalidAge", "Must be 18 or older");
// Database lookup
return entity is null
? new Error("NotFound", "Entity not found")
: entity;
// API response mapping
return result.Match(
value => Results.Ok(value),
error => Results.BadRequest(error));
Optional ASP.NET Core Integration
The published package keeps the core library dependency-free.
If you want a ToHttpResult() extension for Minimal APIs, add the optional HttpExtensions.optional.cs helper from the repository to your ASP.NET Core project and reference the required ASP.NET package there.
return result.ToHttpResult();
Examples
Validation Pattern
public Result<User> ValidateUser(User user)
{
return Result<User>.Success(user)
.Ensure(u => !string.IsNullOrEmpty(u.Email), Errors.InvalidEmail)
.Ensure(u => u.Age >= 18, Errors.UnderAge)
.Ensure(u => u.Email.Contains("@"), Errors.InvalidFormat);
}
Railway-Oriented Programming
public Result<UserDto> CreateUser(CreateUserRequest req)
{
return ValidateRequest(req)
.Bind(r => CheckEmailExists(r.Email))
.Bind(r => HasPermission(r))
.Map(r => new User { Email = r.Email, Name = r.Name })
.Tap(user => _db.Users.Add(user))
.Map(user => new UserDto(user));
}
Performance Characteristics
- Value Type: Stack allocation only, no GC pressure
- No Exceptions in Happy Path: Functional error handling
- AOT Compatible: Full Native AOT support, no reflection
Supported Frameworks
.NET 10.NET 8.0.NET Standard 2.0
Design Philosophy
Brutally minimal. Every line of code must earn its place.
- No async wrappers (use C# async/await natively)
- No advanced LINQ operators
- No abstract base classes
- No external dependencies
- No magic
Feedback
Issues and PRs are welcome on GitHub.
License
MIT — Use freely in personal and commercial projects.
MicroResult: The Result type you won't outgrow. 🚀
| Product | Versions 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 is compatible. 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 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. |
| .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. |
-
.NETStandard 2.0
- No dependencies.
-
net10.0
- No dependencies.
-
net8.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.
Initial release with Result{T}, Error, Match, Map, Bind, Ensure, Tap, and OnFailure.