Quanta 1.0.2
dotnet add package Quanta --version 1.0.2
NuGet\Install-Package Quanta -Version 1.0.2
<PackageReference Include="Quanta" Version="1.0.2" />
<PackageVersion Include="Quanta" Version="1.0.2" />
<PackageReference Include="Quanta" />
paket add Quanta --version 1.0.2
#r "nuget: Quanta, 1.0.2"
#:package Quanta@1.0.2
#addin nuget:?package=Quanta&version=1.0.2
#tool nuget:?package=Quanta&version=1.0.2
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.
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 -
PublishConcurrentAsyncmaximizes throughput;PublishSequentialAsyncguarantees 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 -
RequestAllSequentialAsyncguarantees ordering end-to-end;RequestAllConcurrentAsyncmaximizes throughput and never allocates aTaskfor 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.
Learn more about Target Frameworks and .NET Standard.
-
net10.0
- Quanta.Routing (>= 1.0.2)
-
net8.0
- Quanta.Routing (>= 1.0.2)
-
net9.0
- Quanta.Routing (>= 1.0.2)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.