ResultR v1.0.2 Released: Functional Pattern Matching with Match()

ResultR v1.0.2 is now available. This release introduces a new Match() function that makes working with results feel more functional and expressive—especially in API/controller code where you commonly branch on success vs. failure.
- Release notes: https://github.com/AlanBarber/ResultR/releases/tag/v1.0.2
What’s New in v1.0.2
Pattern Matching with Match()
The new Match() method provides a clean, functional approach to handling results by executing one of two callbacks depending on whether the result is a success or failure.
Instead of manually checking flags or branching with if/else, you can return the outcome directly:
[HttpGet("{id}")]
public async Task<IActionResult> GetUser(int id)
{
var result = await _dispatcher.Dispatch(new GetUserRequest(id));
return result.Match(
user => Ok(user),
error => NotFound(new { error })
);
}
[HttpPost]
public async Task<IActionResult> CreateUser(CreateUserRequest request)
{
var result = await _dispatcher.Dispatch(request);
return result.Match(
user => CreatedAtAction(nameof(GetUser), new { id = user.Id }, user),
error => BadRequest(new { error })
);
}
Why this matters:
- Keeps “happy path” and “error path” in one obvious place
- Encourages a more declarative style
- Reduces boilerplate branching code in handlers/controllers
Accessing Exception Information
When you need more detail on failures, use the overload that provides both the error message and the exception. This is especially useful for logging while still returning a clean response.
[HttpGet("{id}")]
public async Task<IActionResult> GetUser(int id)
{
var result = await _dispatcher.Dispatch(new GetUserRequest(id));
return result.Match(
user => Ok(user),
(error, exception) =>
{
if (exception is not null)
{
_logger.LogError(exception, "Failed to get user {UserId}", id);
}
return NotFound(new { error, hasException = exception is not null });
}
);
}
Match() for Non-Generic Result
Match() is also available on the non-generic Result type—perfect for “command-style” operations where success doesn’t return a value.
public async Task<IActionResult> DeleteUser(int id)
{
var result = await _dispatcher.Dispatch(new DeleteUserRequest(id));
return result.Match(
() => NoContent(),
error => NotFound(new { error })
);
}
// With exception handling
public async Task<IActionResult> DeleteUser(int id)
{
var result = await _dispatcher.Dispatch(new DeleteUserRequest(id));
return result.Match(
() => NoContent(),
(error, exception) =>
{
_logger.LogError(exception, "Failed to delete user {UserId}", id);
return StatusCode(500, new { error });
}
);
}
Try v1.0.2
If you’re already using ResultR, upgrading to v1.0.2 gives you a more fluent way to translate results into HTTP responses (or any other branching logic) without sacrificing clarity.
- Get v1.0.2 / view full notes: https://github.com/AlanBarber/ResultR/releases/tag/v1.0.2
Feedback Welcome
If you try out Match() and have ideas for additional ergonomic improvements (more overloads, async variants, etc.), open an issue or start a discussion on GitHub—feedback helps guide the next releases.
Built with ❤️ for the C# / DotNet community.