AbsurdSdk 1.0.1

dotnet add package AbsurdSdk --version 1.0.1
                    
NuGet\Install-Package AbsurdSdk -Version 1.0.1
                    
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="AbsurdSdk" Version="1.0.1" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="AbsurdSdk" Version="1.0.1" />
                    
Directory.Packages.props
<PackageReference Include="AbsurdSdk" />
                    
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 AbsurdSdk --version 1.0.1
                    
#r "nuget: AbsurdSdk, 1.0.1"
                    
#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 AbsurdSdk@1.0.1
                    
#: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=AbsurdSdk&version=1.0.1
                    
Install as a Cake Addin
#tool nuget:?package=AbsurdSdk&version=1.0.1
                    
Install as a Cake Tool

Absurd.NET

This is a .NET implementation of the Absurd SDK, which has been described at:

1. Setup

To include AbsurdSdk in your project, install the NuGet package using the .NET CLI:

dotnet add package AbsurdSdk

Alternatively, you can use the NuGet Package Manager in Visual Studio:

Install-Package AbsurdSdk

2. Quick Start

We start by defining a IJob, which is going to model an Order Fulfillment Task:

public class FulfillOrderJob : IJob<OrderData, FulfillOrderResult>
{
    private readonly PaymentService _paymentService;
    private readonly ShippingService _shippingService;
    private readonly ILogger<FulfillOrderJob> _logger;

    // Define the registration options here
    public static TaskRegistrationOptions Options => new()
    {
        Name = "fulfill-order",
        Queue = "orders-queue",
        DefaultMaxAttempts = 3
    };

    // Constructor Injection works perfectly here!
    public FulfillOrderJob(
        PaymentService paymentService,
        ShippingService shippingService,
        ILogger<FulfillOrderJob> logger)
    {
        _paymentService = paymentService;
        _shippingService = shippingService;
        _logger = logger;
    }

    public async Task<FulfillOrderResult> ExecuteAsync(TaskContext ctx, OrderData order)
    {
        _logger.LogInformation("Processing Order {OrderId}", order.OrderId);

        // Process the Payment
        PaymentResult payment = await ctx.Step("charge-payment", async () =>
        {
            return await _paymentService.ChargeAsync(order.OrderId, order.Amount);
        });

        if (!payment.Success)
        {
            throw new Exception($"Payment failed: {payment.ErrorMessage}");
        }

        // Wait for Warehouse
        _logger.LogInformation("Waiting for pick signal...");

        JsonNode pickPayload = await ctx.AwaitEvent(
            eventName: $"order-picked:{order.OrderId}",
            stepName: "wait-for-picking"
        );

        // Ship the items
        ShippingResult shipment = await ctx.Step("ship-items", async () =>
        {
            return await _shippingService.ShipAsync(order.OrderId, order.Items);
        });

        return new FulfillOrderResult { Status = "Fulfilled", Tracking = shipment.TrackingNumber };
    }
}

We then register it in the Program.cs like this:


// Register Services
builder.Services.AddSingleton<PaymentService>();
builder.Services.AddSingleton<ShippingService>();

// Build the Absurd Client
builder.Services.AddSingleton<IAbsurd>(sp =>
{
    var logger = sp.GetRequiredService<ILogger<Absurd>>();

    NpgsqlDataSource dataSource = NpgsqlDataSource.Create(DockerContainers.PostgresContainer.GetConnectionString());

    return new Absurd(logger, dataSource);
});

// Register Jobs
builder.Services.RegisterJob<FulfillOrderJob, OrderData, FulfillOrderResult>();

We can then create a Background Worker to poll for Tasks to run:

// Licensed under the MIT license. See LICENSE file in the project root for full license information.

public class SampleOrderWorker : BackgroundService
{
    private readonly IAbsurd _client;
    private readonly IServiceProvider _provider;

    public SampleOrderWorker(IAbsurd client, IServiceProvider provider)
    {
        _client = client;
        _provider = provider;
    }

    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        // Register Jobs
        _client.UseJob<FulfillOrderJob, OrderData, FulfillOrderResult>(_provider);

        // Setup the worker to poll the queue
        AbsurdWorker absurdWorker = new AbsurdWorker(new WorkerOptions
        {
            Queue = "orders-queue",
            WorkerId = "web-worker-01",
            Concurrency = 4,
            PollInterval = 0.5,
            OnError = ex => Console.WriteLine($"[WORKER ERROR] {ex.Message}")
        }, _client);

        // Start the worker loop
        await absurdWorker.ExecuteAsync(stoppingToken);
    }
}

And finally we define two endpoints to create an order and emit events to it:

app.MapPost("/order", async (IAbsurd client, [FromBody] OrderData request) =>
{
    // Start the workflow with explicit options
    var result = await client.SpawnAsync(new SpawnOptions
    {
        Queue = "orders-queue",
        MaxAttempts = 3
    }, "fulfill-order", request);

    return Results.Ok(new { Message = "Order started", RunId = result.RunId });
});

app.MapPost("/order/{orderId}/picked", async (IAbsurd client, string orderId, [FromBody] PickingData data) =>
{
    // This wakes up the suspended task waiting for "order-picked:{orderId}"
    await client.EmitEventAsync(
        eventName: $"order-picked:{orderId}",
        payload: data,
        options: new EmitEventOptions { Queue = "orders-queue" }
    );

    return Results.Ok(new { Message = "Pick signal sent. Workflow will resume." });
});

app.Run();

To kick off an Order send the JSON Payload to the endpoints:

### Creates Order "ORD-123"
POST https://localhost:5000/order
Content-Type: application/json
Accept-Language: en-US,en;q=0.5
{
  "orderId": "ORD-123",
  "amount": 99.50,
  "items": ["Item A", "Item B"]
}

### Continues running Order "ORD-123"
POST https://localhost:5000/order/ORD-123/picked
{ 
  "picker": "Philipp" 
}
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 was computed.  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

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.0.1 39 4/11/2026
1.0.0 46 4/10/2026