RoyalCode.SmartProblems.ApiResults 1.0.0-preview-8.0

This is a prerelease version of RoyalCode.SmartProblems.ApiResults.
dotnet add package RoyalCode.SmartProblems.ApiResults --version 1.0.0-preview-8.0
                    
NuGet\Install-Package RoyalCode.SmartProblems.ApiResults -Version 1.0.0-preview-8.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="RoyalCode.SmartProblems.ApiResults" Version="1.0.0-preview-8.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="RoyalCode.SmartProblems.ApiResults" Version="1.0.0-preview-8.0" />
                    
Directory.Packages.props
<PackageReference Include="RoyalCode.SmartProblems.ApiResults" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add RoyalCode.SmartProblems.ApiResults --version 1.0.0-preview-8.0
                    
#r "nuget: RoyalCode.SmartProblems.ApiResults, 1.0.0-preview-8.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package RoyalCode.SmartProblems.ApiResults@1.0.0-preview-8.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=RoyalCode.SmartProblems.ApiResults&version=1.0.0-preview-8.0&prerelease
                    
Install as a Cake Addin
#tool nuget:?package=RoyalCode.SmartProblems.ApiResults&version=1.0.0-preview-8.0&prerelease
                    
Install as a Cake Tool

SmartProblems

SmartProblems is a small set of .NET libraries for modeling expected failures as values instead of exceptions. It gives you Problem, Problems, Result, Result<T>, FindResult<T>, RFC 9457 ProblemDetails conversion, Minimal API results, Entity Framework helpers, HttpClient deserialization and FluentValidation integration.

Use it when application code should say, plainly:

  • this operation succeeded;
  • this operation failed with one or more typed problems;
  • this entity was not found;
  • this API response should become a standard ProblemDetails payload.

Unexpected failures are still exceptions. Validation errors, business rules, authorization denials, invalid state and "not found" are Problem values.

Target Frameworks

The libraries target:

  • .NET 8
  • .NET 9
  • .NET 10

The test project currently targets .NET 10.

Packages

Install only the packages needed by the project:

dotnet add package RoyalCode.SmartProblems
dotnet add package RoyalCode.SmartProblems.EntityFramework
dotnet add package RoyalCode.SmartProblems.ProblemDetails
dotnet add package RoyalCode.SmartProblems.ApiResults
dotnet add package RoyalCode.SmartProblems.Http
dotnet add package RoyalCode.SmartProblems.FluentValidation

Package overview:

Package Main use
RoyalCode.SmartProblems Core types: Problem, Problems, Result, Result<T>, FindResult<T>, Id<,>
RoyalCode.SmartProblems.EntityFramework EF Core extensions: TryFindAsync, TryFindByAsync, FindByCriteria, AddTo, SaveChanges, RemoveFromAsync
RoyalCode.SmartProblems.ProblemDetails RFC 9457 descriptions, options, conversion setup and problem description pages
RoyalCode.SmartProblems.ApiResults Minimal API/MVC response helpers: OkMatch, CreatedMatch, NoContentMatch, WithExceptionFilter
RoyalCode.SmartProblems.Http HttpClient extensions: ToResultAsync
RoyalCode.SmartProblems.FluentValidation Convert FluentValidation failures to Problems
RoyalCode.SmartProblems.Conversions Lower-level conversion types used by ProblemDetails and Http

Some extension methods intentionally live in Microsoft namespaces after the package is installed:

Member Namespace
EF helpers Microsoft.EntityFrameworkCore
.OkMatch(), .CreatedMatch(), .NoContentMatch() Microsoft.AspNetCore.Http
WithExceptionFilter, MapProblemDetailsDescriptionPage Microsoft.AspNetCore.Builder
AddProblemDetailsDescriptions Microsoft.Extensions.DependencyInjection
ToResultAsync System.Net.Http

Core Usage

Create problems and return them as results:

using RoyalCode.SmartProblems;

public Result<Order> Confirm(Order order)
{
    if (order.IsCanceled)
    {
        return Problems.InvalidState("Canceled orders cannot be confirmed.")
            .With("orderId", order.Id);
    }

    order.Confirm();
    return order;
}

Handle success and failure without exceptions:

Result<Order> result = service.Confirm(order);

if (result.HasProblems(out var problems))
    return problems;

result.EnsureHasValue(out var confirmed);

Common categories and default HTTP status mapping:

Category HTTP status
InvalidParameter 400
ValidationFailed 422
NotAllowed 403
InvalidState 409
NotFound 404
InternalServerError 500
CustomProblem From the registered description

Entity Framework

Install RoyalCode.SmartProblems.EntityFramework and use Microsoft.EntityFrameworkCore.

Lookup by strongly typed id:

using Microsoft.EntityFrameworkCore;
using RoyalCode.SmartProblems.Entities;

Id<Order, int> orderId = id;
FindResult<Order, int> find = await db.TryFindAsync(orderId, ct);

return find.ToResult();

Lookup by one property:

var find = await db.Orders.TryFindByAsync(o => o.Number == number, ct);
return find.ToResult();

Lookup by multiple criteria:

var find = await db.FindByCriteria<City>()
    .By(c => c.StateId, stateId)
    .By(c => c.Name, name)
    .TryFindAsync(ct);

return find.ToResult();

Persist through Result pipelines:

return await Product.Create(command)
    .AddTo(db)
    .SaveChangesAsync(db, ct);

Remove only when the entity is found:

return await db.Products
    .TryFindByAsync(p => p.Id == id, ct)
    .RemoveFromAsync(db, ct)
    .SaveChangesAsync(db, ct);

Minimal APIs

Install RoyalCode.SmartProblems.ApiResults.

Use OkMatch<T>, CreatedMatch<T> and NoContentMatch to return either the success response or a ProblemDetails response from the same endpoint.

using Microsoft.AspNetCore.Http;
using RoyalCode.SmartProblems.HttpResults;

var group = app.MapGroup("/api/orders")
    .WithExceptionFilter();

group.MapGet("/{id:int}",
    async Task<OkMatch<OrderDto>> (int id, OrdersService service, CancellationToken ct)
        => await service.GetAsync(id, ct));

group.MapPost("/",
    async Task<CreatedMatch<OrderDto>> (CreateOrder command, OrdersService service, CancellationToken ct)
        => (await service.CreateAsync(command, ct))
            .CreatedMatch(order => $"/api/orders/{order.Id}"));

group.MapDelete("/{id:int}",
    async Task<NoContentMatch> (int id, OrdersService service, CancellationToken ct)
        => await service.DeleteAsync(id, ct));

WithExceptionFilter is for unexpected exceptions at the HTTP boundary. Keep expected failures in Result, Problem or Problems.

ProblemDetails and Problem Type Documentation

Install RoyalCode.SmartProblems.ProblemDetails.

Register descriptions for known problem types, especially custom typeId values, and optionally expose an HTML catalog for consumers.

using System.Net;
using Microsoft.Extensions.DependencyInjection;
using RoyalCode.SmartProblems.Descriptions;

builder.Services.AddProblemDetailsDescriptions(options =>
{
    options.Descriptor.Add(new ProblemDetailsDescription(
        typeId: "order-on-hold",
        title: "Order on hold",
        description: "The order cannot move forward while it is on hold.",
        status: HttpStatusCode.Conflict));
});

app.MapProblemDetailsDescriptionPage(); // GET /.problems

Create a custom problem using the same typeId:

return Problems.Custom(
    "The order is on hold.",
    typeId: "order-on-hold",
    property: "status");

HttpClient

Install RoyalCode.SmartProblems.Http.

ToResultAsync reads successful responses as values and failed application/problem+json responses as Problems.

HttpResponseMessage response = await http.GetAsync("/api/orders/123", ct);
Result<OrderDto> result = await response.ToResultAsync<OrderDto>(ct: ct);

if (result.HasValue(out var order))
{
    return order;
}

return result;

FluentValidation

Install RoyalCode.SmartProblems.FluentValidation.

Result<CreateOrder> valid = await validator.EnsureIsValidAsync(command);

if (valid.HasProblems(out var problems))
    return problems;

Documentation

Project documentation lives under src/.docs:

Prefer the XML documentation exposed by the installed packages for exact method signatures. The docs above explain the intended usage and composition style.

Development

From the repository root:

dotnet test src/RoyalCode.SmartProblems.Tests/RoyalCode.SmartProblems.Tests.csproj

The tests cover the core result types, problem conversion, API results, exception filters, Entity Framework helpers, HttpClient conversion, FluentValidation integration and problem description pages.

License

See LICENSE.

Product Compatible and additional computed target framework versions.
.NET 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 is compatible.  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. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages (2)

Showing the top 2 NuGet packages that depend on RoyalCode.SmartProblems.ApiResults:

Package Downloads
RoyalCode.SmartSearch.AspNetCore

Package Description

RoyalCode.SmartCommands

Runtime contracts and attributes for generated command handlers, validation pipelines, persistence access and Minimal API mapping.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.0.0-preview-8.0 86 7/19/2026
1.0.0-preview-7.0 61 7/10/2026
1.0.0-preview-6.0 152 1/6/2026
1.0.0-preview-5.0 204 11/3/2025
1.0.0-preview-4.9 182 10/23/2025
1.0.0-preview-4.8 132 10/3/2025
1.0.0-preview-4.7 122 9/5/2025
1.0.0-preview-4.6 110 9/5/2025
1.0.0-preview-4.5 169 8/21/2025
1.0.0-preview-4.4 353 7/21/2025
1.0.0-preview-4.3 167 7/18/2025
1.0.0-preview-4.2 163 7/16/2025
1.0.0-preview-4.0 173 7/9/2025
1.0.0-preview-3.5 165 6/20/2025
1.0.0-preview-3.0 111 5/2/2025
1.0.0-preview-2.0 186 4/21/2025
1.0.0-preview-1.4 124 9/25/2024
1.0.0-preview-1.3 121 9/24/2024
1.0.0-preview-1.2 152 8/12/2024
1.0.0-preview-1.1 107 7/31/2024
Loading failed