Skip to content

Advanced Usage

9.3.1. CancellationToken Support

v1.27.0 — When a service method declares CancellationToken cancellationToken = default, SmartEndpoints detects it and injects the cancellation token through the generated lambda automatically. Methods without this parameter are unaffected — fully backward-compatible.

[AutoGenerateEndpoints(RoutePrefix = "/api/users")]
public class UserController
{
    // CancellationToken detected — generator threads it through
    public async Task<Result<User>> GetUser(int id, CancellationToken cancellationToken = default)
        => await _service.GetByIdAsync(id, cancellationToken);

    // No CancellationToken — generated normally, no change
    public async Task<Result<List<User>>> GetUsers()
        => await _service.GetAllAsync();
}

Generated lambdas (v1.27.0+):

// With CancellationToken — ct injected by ASP.NET Minimal API
userGroup.MapGet("/{id}", async (int id, UserController service, CancellationToken ct) =>
{
    var result = await service.GetUser(id, ct);
    return result.ToIResult();
});

// Without CancellationToken — unchanged
userGroup.MapGet("", async (UserController service) =>
{
    var result = await service.GetUsers();
    return result.ToIResult();
});

9.3.2. FluentValidation Bridge

⚠️ Optional — migration bridge only. Not needed for new projects.

REslava.Result already includes full native validation via [Validate] (DataAnnotations → Result<T>) and the Validation DSL (19 fluent rules). This package exists only for teams that already have FluentValidation validators and want to adopt REslava.Result without rewriting them. New projects should use [Validate] or the Validation DSL instead.

v1.28.0 — For teams migrating from FluentValidation, the REslava.Result.FluentValidation package emits .Validate(IValidator<T>) extensions and integrates with SmartEndpoints — existing validators require zero changes:

dotnet add package REslava.Result.FluentValidation
dotnet add package FluentValidation   # your existing validators
using REslava.Result.FluentValidation;

[FluentValidate]   // ← swap [Validate] for [FluentValidate] on existing FV types
public record CreateOrderRequest(string CustomerId, decimal Amount);

// Your AbstractValidator<T> stays unchanged
public class CreateOrderRequestValidator : AbstractValidator<CreateOrderRequest>
{
    public CreateOrderRequestValidator()
    {
        RuleFor(x => x.CustomerId).NotEmpty();
        RuleFor(x => x.Amount).GreaterThan(0);
    }
}

Generated lambda (v1.28.0+):

ordersGroup.MapPost("", async (
    CreateOrderRequest req,
    IValidator<CreateOrderRequest> reqValidator,   // ← auto-injected from DI
    IOrderService svc,
    CancellationToken cancellationToken) =>
{
    var validation = req.Validate(reqValidator);   // ← uses FluentValidation internally
    if (!validation.IsSuccess) return validation.ToIResult();

    var result = await svc.CreateOrder(req, cancellationToken);
    return result.ToIResult();
});

Register in DI once — SmartEndpoints handles the rest:

builder.Services.AddScoped<IValidator<CreateOrderRequest>, CreateOrderRequestValidator>();

> Note: [FluentValidate] and [Validate] cannot be applied to the same type (RESL1006 compile error). Choose the bridge for existing FV validators; use [Validate] for new types with DataAnnotations.