Scaffold and extend Result Pattern types in .NET projects. Use this skill when adding new Result variants, extending Result<T> with new mapping methods, or integrating the Result Pattern into new service/repository layers.
This skill helps scaffold and extend the Result Pattern in ASP.NET Core Minimal API projects. It covers creating Result<T> types, HTTP response mapping extensions, and wiring services that return results instead of throwing exceptions.
dotnet CLI available on your PATHA well-formed Result<T> type must provide:
| Member | Purpose |
|---|---|
IsSuccess / IsFailure | Status check |
Value | Accessible only on success; throws on failure |
Error | Accessible only on failure; throws on success |
Success(T value) | Factory for happy path |
Failure(string error) | Factory for expected failure |
implicit operator | Auto-wrap T → Result<T>.Success(T) |
| Scenario | Approach |
|---|---|
| Validation error | Result<T>.Failure(...) |
| Entity not found | Result<T>.Failure(...) |
| Duplicate / conflict | Result<T>.Failure(...) |
| Database connection lost | Throw exception |
| Null argument to public API | Throw ArgumentNullException |
Define the model as a record in Models/:
public record CreateProductRequest(string Name, decimal Price);
public record Product { public int Id { get; set; } public string Name { get; set; } }
Add repository interface & implementation returning Result<T>:
public interface IProductRepository
{
Task<Result<Product>> GetByIdAsync(int id);
Task<Result<Product>> AddAsync(Product product);
}
Add service with validation returning Result<T>:
public class ProductService(IProductRepository repo) : IProductService
{
public async Task<Result<Product>> CreateAsync(CreateProductRequest request)
{
if (string.IsNullOrWhiteSpace(request.Name))
return Result<Product>.Failure("Name is required");
var product = new Product { Id = Random.Shared.Next(1, 1000), Name = request.Name };
return await repo.AddAsync(product);
}
}
Map the endpoint in Program.cs:
products.MapPost("/", async (CreateProductRequest req, IProductService svc) =>
{
var result = await svc.CreateAsync(req);
return result.ToHttpResponse();
});
When you need a new HTTP status mapping (e.g., Conflict), add a method to ResultExtensions:
public static Results<Ok<T>, Conflict<ErrorResponse>, BadRequest<ErrorResponse>> ToHttpResponseWithConflict<T>(
this Result<T> result)
{
if (result.IsSuccess)
return TypedResults.Ok(result.Value);
var isConflict = result.Error.Contains("already exists", StringComparison.OrdinalIgnoreCase);
return isConflict
? TypedResults.Conflict(new ErrorResponse(result.Error))
: TypedResults.BadRequest(new ErrorResponse(result.Error));
}
After scaffolding, always:
dotnet build — ensure zero errors/warnings