Mitc.Support.Results 1.1.0
Mitc.Support.Results
Generic result types with monadic composition for explicit error handling. Services return results instead of throwing exceptions, making failure a first-class part of the method signature.
Core Types
| Type | Purpose |
|---|---|
Result<TValue> |
Operation that returns a value or a string error |
Outcome |
Operation that succeeds or fails with no return value |
StructuredResult<TValue> |
Like Result<TValue> but with structured errors (ResultErrors) |
Result<TValue, TError> |
Bring your own error type |
Outcome<TError> |
Bring your own error type, no return value |
All result types carry a ResultStatus that classifies the outcome:
public enum ResultStatus
{
Success,
NotFound,
Invalid,
Unauthorized,
Conflict,
}
Basic Usage
Returning results from services
The Result static class provides shorthand factory methods. The method's return type drives the implicit conversion:
public async Task<Result<User>> GetByIdAsync(UserId id)
{
var user = await context.Users.FindAsync(id);
if (user is null)
return Result.NotFound("User not found.");
return user; // implicit Success
}
public async Task<Outcome> DeleteAsync(UserId id)
{
var user = await context.Users.FirstOrDefaultAsync(u => u.Id == id);
if (user is null)
return Result.NotFound("User not found.");
context.Users.Remove(user);
await context.SaveChangesAsync();
return Outcome.Success();
}
The same Result.NotFound(...) call works whether the method returns Result<User>, Outcome, or StructuredResult<User>.
Implicit conversions
For Result<TValue> where TValue is not string, implicit conversions keep service code clean:
Result<int> success = 42; // implicit Success
Result<int> failure = "Not found."; // implicit Invalid
Outcome failure = "Bad request."; // implicit Invalid
When TValue is string, use explicit factory methods to avoid ambiguity:
var result = Result<string>.Success("Alice");
var error = Result.NotFound("User not found.");
Match — handle both outcomes
// Returns a value
var response = result.Match(
success: user => Ok(user),
failure: error => NotFound(error));
// Outcome (no value)
var response = outcome.Match(
success: () => NoContent(),
failure: error => BadRequest(error));
Switch — side effects
result.Switch(
success: user => logger.LogInformation("Found {User}", user.Name),
failure: error => logger.LogWarning("Failed: {Error}", error));
Composition
Map — transform the success value
Result<string> userName = result.Map(user => user.Name);
// Success -> transformed value; Failure -> passes through unchanged
Bind — chain result-returning operations
Result<Order> order = await GetUser(id)
.Bind(user => GetActiveOrder(user));
// Short-circuits on first failure
OnSuccess — side effect on success
result.OnSuccess(user => cache.Set(user.Id, user));
// Executes only on success, returns self for chaining
Async chaining
All composition methods work directly on Task<Result<T>>:
var name = await userService.GetByIdAsync(id)
.Map(user => user.Name);
Structured Errors
When you need more than a string error — machine-readable codes, field-level validation errors — use StructuredResult<TValue> with ResultError:
public async Task<StructuredResult<User>> CreateAsync(CreateRequest request)
{
if (await EmailExists(request.Email))
return Result.Conflict(new ResultError("Already in use.", "Email", "DUPLICATE_EMAIL"));
// ...
return user;
}
ResultError constructors
new ResultError("Something went wrong.") // message only
new ResultError("Already in use.", "Email") // message + property
new ResultError("Already in use.", "Email", "DUPLICATE_EMAIL") // message + property + code
Multiple errors
return Result.Invalid(ResultErrors.From(
new ResultError("Name is required.", "Name"),
new ResultError("Email is invalid.", "Email")));
ResultErrors helper factories
ResultErrors.Single("Something went wrong.")
ResultErrors.Single("Email", "Already in use.")
ResultErrors.Single(new ResultError("...", "Email", "DUPLICATE"))
ResultErrors.From(error1, error2, error3)
Failure Shorthand
The non-generic Result class returns lightweight Failure / StructuredFailure structs that implicitly convert to the method's return type:
// String errors — converts to Result<T>, Outcome, or StructuredResult<T>
Result.NotFound("User not found.")
Result.Invalid("Bad request.")
Result.Unauthorized("No access.")
Result.Conflict("Duplicate detected.")
// Structured errors — converts to StructuredResult<T>
Result.NotFound(new ResultError("Not found.", "Id", "NOT_FOUND"))
Result.Invalid(ResultErrors.From(error1, error2))
Custom Error Types
Use Result<TValue, TError> and Outcome<TError> with any error type:
Result<User, AppError> result = await GetUser(id);
Outcome<AppError> outcome = await DeleteUser(id);
Status Inspection
Every result exposes its status:
result.IsSuccess // true if Status == ResultStatus.Success
result.Status // the ResultStatus enum value
result.Value // the success value (check IsSuccess first)
result.Error // the error (check IsSuccess first)
Match is the recommended way to access values — it forces you to handle both cases.
Type Hierarchy
Result<TStatus, TValue, TError> abstract base
├── Result<TValue, TError> custom error type, ResultStatus
│ ├── Result<TValue> string errors
│ └── StructuredResult<TValue> ResultErrors
└── Outcome<TError> no return value, custom error type
└── Outcome no return value, string errors
Result (static) shorthand factory methods
├── returns Failure converts to Result<T>, Outcome, StructuredResult<T>
└── returns StructuredFailure converts to StructuredResult<T>
Target Frameworks
netstandard2.0, net5.0, net6.0, net7.0, net8.0, net9.0, net10.0
Showing the top 20 packages that depend on Mitc.Support.Results.
| Packages | Downloads |
|---|---|
|
Mitc.Integrations.Stripe
Turnkey Stripe integration for ASP.NET Core. Handles webhook processing, product/price catalog syncing, subscription management, checkout sessions, and metered usage reporting.
|
0 |
.NET 5.0
- No dependencies.
.NET 6.0
- No dependencies.
.NET 7.0
- No dependencies.
.NET 8.0
- No dependencies.
.NET 9.0
- No dependencies.
.NET 10.0
- No dependencies.
.NET Standard 2.0
- No dependencies.