Quanta 1.0.2

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

Quanta

High-performance in-process message routing for modern .NET applications.

Quanta is a lightweight, low-allocation messaging library for high-throughput, low-latency communication between components inside a .NET application. It supports publish/subscribe and request/reply patterns with both synchronous and asynchronous handlers.

NuGet License: MIT

Installation

dotnet add package Quanta

Targets .NET 8, .NET 9, and .NET 10.


Publish / Subscribe

public record OrderPlaced(Guid OrderId, decimal Total) : IMessage;

var router = new MessageRouter();

var token = router.Subscribe<OrderPlaced>((in msg, ctx) =>
    Console.WriteLine($"Order {msg.OrderId} - {msg.Total:C}"));

router.Publish(new OrderPlaced(Guid.NewGuid(), 49.99m));

router.Unsubscribe(token);

Async handlers

Use SubscribeAsync to register async handlers, then dispatch with PublishConcurrentAsync or PublishSequentialAsync:

// Async handlers require a named local function - `in` is not allowed on async lambdas.
ValueTask HandleOrder(in OrderPlaced msg, MessageContext ctx, CancellationToken ct) =>
    SaveToDbAsync(msg, ct);

router.SubscribeAsync<OrderPlaced>(HandleOrder);

// All handlers start simultaneously - completion order is non-deterministic.
await router.PublishConcurrentAsync(new OrderPlaced(...));

// Each handler is fully awaited before the next one starts - strict priority order.
await router.PublishSequentialAsync(new OrderPlaced(...));

Concurrent vs Sequential - PublishConcurrentAsync maximizes throughput; PublishSequentialAsync guarantees that priority ordering is respected end-to-end even for async handlers.


Request / Reply

RequestFirst dispatches to the highest-priority registered handler and returns its response wrapped in Option<T>.

public record PriceQuery(Guid ProductId) : IMessage;
public record PriceResult(decimal Price) : IMessage;

var router = new RequestRouter();

router.Subscribe<PriceQuery, PriceResult>((in req, ctx) =>
    new PriceResult(42.00m));

var result = router.RequestFirst<PriceQuery, PriceResult>(new PriceQuery(productId));

if (result.TryGetValue(out var price))
    Console.WriteLine(price.Price);

IRequest<TResponse> shorthand

Implementing IRequest<TResponse> on the request type lets you omit TRequest at the call site:

public record PriceQuery(Guid ProductId) : IMessage, IRequest<PriceResult>;

// TRequest is inferred - only TResponse needs to be specified.
var result = router.RequestFirst<PriceResult>(new PriceQuery(productId));

Querying all handlers

RequestAll collects responses from every registered handler in descending priority order:

var results = router.RequestAll<PriceQuery, PriceResult>(new PriceQuery(productId));

foreach (var r in results)
    if (r.TryGetValue(out var price))
        Console.WriteLine(price.Price);

Async request dispatch

ValueTask<PriceResult> FetchPrice(in PriceQuery req, MessageContext ctx, CancellationToken ct) =>
    FetchPriceAsync(req.ProductId, ct);

router.SubscribeAsync<PriceQuery, PriceResult>(FetchPrice);

// Each handler is awaited in turn - priority respected end-to-end.
var seq = await router.RequestAllSequentialAsync<PriceQuery, PriceResult>(new PriceQuery(id));

// All handlers fire concurrently - results are returned in priority order.
var con = await router.RequestAllConcurrentAsync<PriceQuery, PriceResult>(new PriceQuery(id));

Sequential vs Concurrent - RequestAllSequentialAsync guarantees ordering end-to-end; RequestAllConcurrentAsync maximizes throughput and never allocates a Task for handlers that complete synchronously.


Channels

Channels route messages to a specific subset of subscribers instead of broadcasting to all.

var adminChannel = NamedChannel.Create("admin");

// Bind a handler to the admin channel
router.Subscribe<OrderPlaced>((in msg, ctx) => { ... },
    new SubscribeConfig { LinkedChannel = adminChannel });

// Broadcast - reaches all subscribers regardless of channel binding
router.Publish(new OrderPlaced(...));

// Targeted - only subscribers bound to adminChannel receive the message
router.PublishOn(adminChannel, new OrderPlaced(...));

The same channel targeting is available for async dispatch (PublishOnConcurrentAsync, PublishOnSequentialAsync) and for request/reply (RequestFirstOn, RequestAllOn, RequestFirstOnAsync, RequestAllOnSequentialAsync, RequestAllOnConcurrentAsync).

Point-to-point

SendTo and RequestTo deliver directly to a single subscriber identified by its subscription token:

var token = router.Subscribe<OrderPlaced>(...);

// MessageRouter - no response
router.SendTo(token, new OrderPlaced(...));

// RequestRouter - returns Option<TResponse>
var requestToken = requestRouter.Subscribe<PriceQuery, PriceResult>(...);
var result = requestRouter.RequestTo<PriceQuery, PriceResult>(requestToken, new PriceQuery(id));

Global bus

Bus provides a process-wide default router pair — no dependency-injection container required.

// Publish / subscribe
Bus.Messages.Subscribe<OrderPlaced>((in msg, ctx) => Console.WriteLine(msg.OrderId));
Bus.Messages.Publish(new OrderPlaced(Guid.NewGuid(), 49.99m));

// Request / reply
Bus.Requests.Subscribe<PriceQuery, PriceResult>((in req, ctx) => new PriceResult(42.00m));
var result = Bus.Requests.RequestFirst<PriceQuery, PriceResult>(new PriceQuery(productId));

Call Bus.Configure at startup to substitute your own implementations (decorated routers, test doubles, etc.):

Bus.Configure(myMessageRouter, myRequestRouter);

Priority

Higher priority values are invoked first. Handlers with equal priority follow FIFO registration order.

router.Subscribe<OrderPlaced>(handler, new SubscribeConfig { Priority = 10 });

License

MIT - see LICENSE.

There are no supported framework assets in this 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.2 181 3/13/2026
1.0.1 166 3/13/2026
1.0.0 166 3/13/2026