Source generation
Dispatcher ships two registration and dispatch implementations behind the same public API. Your messages, handlers, and pipeline behaviors are written identically in both. What changes is how handlers are discovered and when route errors surface.
| Reflection | Source generation | |
|---|---|---|
| Package | Send0xx.Dispatcher.DependencyInjection |
Send0xx.Dispatcher.SourceGeneration |
| Handlers discovered | At startup, by scanning assemblies | At compile time |
| Opt-in | AddDispatcherHandlers(assembly) |
[assembly: GenerateDispatcherHandlers("…")] |
| Trimming / Native AOT | Not supported | Supported |
| Route errors surface | At startup | At build time |
| Manual route target | AddDispatcherMessage<T>() |
Not applicable |
Tip
If you publish with Native AOT or trimming, use source generation. Otherwise start with reflection and switch later: it is a package swap plus two assembly attributes, and your handlers do not change.
Send0xx.Dispatcher.SourceGeneration generates typed handler registrations and a dispatcher
implementation. Reflection is not used for registration or dispatch, and the package does not
reference the reflection-based implementation at all, so there is no reflective code path for the
trimmer to preserve.
Single-project setup
Opt in at assembly level and give the generated extension methods unique names:
using Dispatcher;
using Dispatcher.SourceGeneration;
[assembly: GenerateDispatcherHandlers("AddApplicationHandlers")]
[assembly: GenerateDispatcher("AddDispatcher")]
builder.Services
.AddDispatcher()
.AddApplicationHandlers();
Handlers may remain internal. The generator discovers queries, commands, notifications, and pipeline behaviors at compile time and emits explicit DI registrations and frozen dispatch tables.
Lifetimes
The generated AddDispatcher accepts the same options as the reflection-based one, and the
same lifetime rules apply:
builder.Services
.AddDispatcher(options =>
options.ServiceLifetime = ServiceLifetime.Transient)
.AddApplicationHandlers(options =>
options.ServiceLifetime = ServiceLifetime.Singleton);
Dispatcher and generated handler lifetimes are configured independently.
Multi-assembly composition
Each referenced assembly can generate its own handler-registration method while the host generates the dispatcher:
// In a referenced handlers assembly
[assembly: GenerateDispatcherHandlers("AddOrderHandlers")]
// In the host assembly
[assembly: GenerateDispatcher("AddDispatcher")]
builder.Services
.AddDispatcher()
.AddOrderHandlers();
Modular composition is supported, but not required.
Routes must be known at build time
Important
Source-generated routes must be known at build time from the generated host, a generated handler
module, or an assembly that declares one of the handled message types. There is no equivalent of
AddDispatcherMessage<T>().
See route targets.