Trax.Mediator
1.23.2
Prefix Reserved
See the version list below for details.
dotnet add package Trax.Mediator --version 1.23.2
NuGet\Install-Package Trax.Mediator -Version 1.23.2
<PackageReference Include="Trax.Mediator" Version="1.23.2" />
<PackageVersion Include="Trax.Mediator" Version="1.23.2" />
<PackageReference Include="Trax.Mediator" />
paket add Trax.Mediator --version 1.23.2
#r "nuget: Trax.Mediator, 1.23.2"
#:package Trax.Mediator@1.23.2
#addin nuget:?package=Trax.Mediator&version=1.23.2
#tool nuget:?package=Trax.Mediator&version=1.23.2
Trax.Mediator
Dispatch station for Trax trains. Hand it the cargo and it routes it to the right train, no need to know which train handles what.
Trax is a .NET framework for writing business operations as trains: a ServiceTrain<TIn, TOut> runs a chain of junctions, and every run is recorded. Trax.Mediator adds the ITrainBus, which finds the train registered for an input's type and runs it, so callers depend on the input type rather than on a train class. It also scans assemblies to register trains, keeps the registry of known trains, enforces [TraxAuthorize] on trains, and caps concurrent runs.
dotnet add package Trax.Mediator
dotnet add package Trax.Effect.Data.InMemory # or Trax.Effect.Data.Postgres
A minimal host: one train, registered by scanning, run through the bus.
using LanguageExt;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Trax.Core.Junction;
using Trax.Effect.Data.InMemory.Extensions;
using Trax.Effect.Extensions;
using Trax.Effect.Services.ServiceTrain;
using Trax.Mediator.Extensions;
using Trax.Mediator.Services.TrainBus;
var builder = Host.CreateApplicationBuilder(args);
builder.Services.AddTrax(trax =>
trax.AddEffects(effects => effects.UseInMemory())
.AddMediator(mediator => mediator.ScanAssemblies(typeof(Program).Assembly))
);
using var host = builder.Build();
await host.StartAsync(); // startup checks every train's chain and refuses to start on a bad one
using var scope = host.Services.CreateScope();
var trainBus = scope.ServiceProvider.GetRequiredService<ITrainBus>();
var greeting = await trainBus.RunAsync<Greeting>(new GreetRequest("Jane"));
Console.WriteLine(greeting.Message); // Hello, Jane!
public record GreetRequest(string Name);
public record Greeting(string Message);
// Every train needs its own interface; the bus resolves the train through it.
public interface IGreetTrain : IServiceTrain<GreetRequest, Greeting>;
public class GreetTrain : ServiceTrain<GreetRequest, Greeting>, IGreetTrain
{
protected override Task<Either<Exception, Greeting>> Junctions() =>
Chain<BuildGreetingJunction>().Resolve();
}
public class BuildGreetingJunction : Junction<GreetRequest, Greeting>
{
public override Task<Greeting> Run(GreetRequest input) =>
Task.FromResult(new Greeting($"Hello, {input.Name}!"));
}
ITrainBus is scoped, so resolve it from a scope (in ASP.NET Core, inject it into a controller or endpoint). The documentation is at traxsharp.net/docs.
The Trax Stack
Trax is a layered framework split across several repos. You can stop at whatever layer solves your problem. You are here: Trax.Mediator.
| Repo | Adds |
|---|---|
| Trax.Core | Pipelines, junctions, railway error propagation |
| Trax.Effect | Execution logging, DI, pluggable storage |
| Trax.Mediator | Decoupled dispatch via TrainBus |
| Trax.Scheduler | Cron schedules, retries, dead-letter queues |
| Trax.Api | GraphQL API for remote access |
| Trax.Dashboard | Blazor monitoring UI |
| Trax.Cli | trax-cli project scaffolding tool |
| Trax.Samples | Sample apps and a dotnet new template |
Full documentation: traxsharp.net/docs.
The Problem
When one part of your system needs to send a train, it has to know exactly which train class to use. Controllers depend on concrete train types, stops that trigger other trains need direct references, and everything gets coupled together.
With TrainBus
TrainBus is a dispatch station. At startup it builds a map of what cargo goes on which train. To send something, you just drop off the cargo:
public class OrderController(ITrainBus trainBus) : ControllerBase
{
[HttpPost]
public async Task<IActionResult> Create(OrderRequest request)
{
var receipt = await trainBus.RunAsync<OrderReceipt>(request);
return Ok(receipt);
}
}
No reference to ProcessOrderTrain. The dispatch station looks at the cargo type and sends it on the right train.
Installation
dotnet add package Trax.Mediator
Trax.Mediator depends on Trax.Effect, which depends on Trax.Core. Both are pulled in transitively.
For tests, Trax.Mediator.Testing ships TrainGuards.EveryTrainHasInterface(assemblies), an architecture guard that lists every ServiceTrain missing its I{Name} interface:
dotnet add package Trax.Mediator.Testing
Setup
Register your train assemblies during startup. The dispatch station scans them for all IServiceTrain<TIn, TOut> implementations and builds the cargo-to-train map automatically:
builder.Services.AddTrax(trax =>
trax.AddEffects(effects => effects.UsePostgres(connectionString))
.AddMediator(typeof(Program).Assembly)
);
Multiple assemblies:
builder.Services.AddTrax(trax =>
trax.AddEffects(effects => effects.UsePostgres(connectionString))
.AddMediator(
typeof(Program).Assembly,
typeof(SomeTrainInAnotherProject).Assembly
)
);
That's it. Every IServiceTrain<TIn, TOut> in those assemblies is now dispatchable through ITrainBus.
Usage
Dispatching a train
// With a return value: send cargo, get a delivery back
var user = await trainBus.RunAsync<User>(new CreateUserRequest
{
Email = "jane@example.com",
Name = "Jane"
});
// One-way: send cargo, no delivery expected
await trainBus.RunAsync(new SendNotificationRequest { UserId = userId });
Cancellation
var receipt = await trainBus.RunAsync<OrderReceipt>(request, cancellationToken);
The cancellation signal is forwarded to the train and all its stops.
Nested trains
A stop can dispatch another train mid-journey. The nested train records its own run, which is not linked to the outer one: the bus does not set its ParentId. Do not pass the current train's Metadata to try to link them; the metadata argument is for running as a pre-created Pending record, and a running train's metadata is refused with a TrainException.
public class SendWelcomeEmailJunction(ITrainBus trainBus) : Junction<User, Unit>
{
public override async Task<Unit> Run(User input)
{
await trainBus.RunAsync(new SendEmailRequest
{
To = input.Email,
Template = "welcome"
});
return Unit.Default;
}
}
The departure board
The dispatch station exposes a registry of all known trains, which is useful for tooling and dashboards:
public class TrainListEndpoint(ITrainRegistry registry)
{
public IEnumerable<string> GetTrainNames()
=> registry.InputTypeToTrain.Values.Select(t => t.Name);
}
How It Works
At startup, AddMediator scans the provided assemblies for types implementing IServiceTrain<TIn, TOut>. It builds a dictionary from cargo type (input) to train type and registers each train in the DI container. When you call RunAsync<TOut>(input), the dispatch station looks up input.GetType(), resolves the matching train from DI, and sends it on its way.
Next Layer
When you need recurring background jobs with retries and dead-lettering, move up to Trax.Scheduler.
License
MIT
Trademark & Brand Notice
Trax is an open-source .NET framework provided by TraxSharp. This project is an independent community effort and is not affiliated with, sponsored by, or endorsed by the Utah Transit Authority, Trax Retail, or any other entity using the "Trax" name in other industries.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | 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. |
-
net10.0
- Microsoft.Extensions.DependencyInjection (>= 10.0.12)
- Trax.Effect (>= 1.57.3)
- Trax.Effect.Data (>= 1.57.3)
NuGet packages (5)
Showing the top 5 NuGet packages that depend on Trax.Mediator:
| Package | Downloads |
|---|---|
|
Trax.Scheduler
Recurring and one-off scheduling for Trax .NET trains: cron and interval manifests, a durable work queue, retries, dead-lettering and dependent trains. Requires a persistent Trax data provider such as Trax.Effect.Data.Postgres. |
|
|
Trax.Api
Core services behind the Trax .NET GraphQL API: health checks, per-train authorization, the HTTP principal provider and the shared DTOs. Trax.Api.GraphQL brings it in; reference it directly only to use those services without the GraphQL schema. |
|
|
Trax.Api.Auth
Core principal abstraction (TraxPrincipal, ITraxPrincipalResolver) and claim-type constants shared by the Trax .NET auth schemes. The scheme packages bring it in; reference it directly to write a resolver or inject TraxPrincipal. NO WARRANTY. Trax auth is plumbing, not a security product. You are solely responsible for securing systems that use it. See SECURITY-DISCLAIMER.md. |
|
|
Trax.Dashboard
Blazor Server operations dashboard for Trax .NET, mounted into your existing ASP.NET Core host at /trax: trains, run metadata and logs, scheduler manifests and groups, dead letters, the work queue, and runtime effect settings. Install it in the app that already calls AddTrax, then call builder.AddTraxDashboard() and app.UseTraxDashboard(). |
|
|
Trax.Api.GraphQL.Client.Trax
Trax .NET integration layer for Trax.Api.GraphQL.Client: AssemblySchemaProvider, GraphQLQueryJunction, log correlation, dashboard discovery. |
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 1.62.0 | 7 | 10/9/2026 |
| 1.61.0 | 298 | 10/6/2026 |
| 1.24.2 | 526 | 10/6/2026 |
| 1.24.1 | 756 | 10/4/2026 |
| 1.24.0 | 330 | 10/2/2026 |
| 1.23.3 | 340 | 10/1/2026 |
| 1.23.2 | 247 | 9/30/2026 |
| 1.23.1 | 209 | 9/29/2026 |
| 1.23.0 | 802 | 9/28/2026 |
| 1.22.1 | 700 | 9/24/2026 |
| 1.22.0 | 2,308 | 9/15/2026 |
| 1.21.0 | 1,408 | 8/13/2026 |
| 1.20.0 | 5,688 | 6/5/2026 |
| 1.19.0 | 649 | 6/2/2026 |
| 1.18.0 | 660 | 6/2/2026 |
| 1.17.0 | 4,217 | 5/4/2026 |
| 1.16.1 | 902 | 4/15/2026 |
| 1.16.0 | 767 | 4/15/2026 |
| 1.15.0 | 1,564 | 4/1/2026 |
| 1.14.1 | 929 | 3/19/2026 |