Title: Add source generator to eliminate reflection-based dispatch and enable NativeAOT
Labels: enhancement, mediator
Body:
Problem
Mediator.cs uses runtime reflection with ConcurrentDictionary<Type, MethodInfo> caches to dispatch commands, queries, and stream queries when the concrete type is only known at runtime (the single-generic-parameter overloads like SendCommandAsync<TResult>(ICommand<TResult>)). This has several drawbacks:
- Cold-start overhead from reflection and
MakeGenericMethod on first call per type
- No NativeAOT / trimming support -- the reflection patterns are not trim-safe
- No compile-time validation -- a missing handler registration only fails at runtime
- Allocation pressure from
new object[] { command, cancellationToken } on every reflected dispatch
Proposed Solution
Create a Cortex.Mediator.SourceGenerator package that uses Roslyn incremental source generators to:
- Scan the compilation for all
ICommand<T>, IQuery<T>, IStreamQuery<T>, and INotification implementations
- Generate a
GeneratedMediator class with strongly-typed dispatch (no reflection)
- Generate DI registration code (
AddCortexMediator()) that registers all handlers without Scrutor
- Emit build warnings/errors for commands/queries without matching handlers
- Support
[assembly: MediatorAssembly] or similar attribute to control scanning scope
Benefits
- Zero reflection at runtime
- Full NativeAOT and trimming compatibility
- Missing handler = compile error
- Removes Scrutor dependency when using source gen
- Aligns with .NET ecosystem direction (similar approach to
Mediator by martinothamar)
Scope
- New project:
Cortex.Mediator.SourceGenerator
- The existing reflection-based
Mediator class should remain as a fallback for dynamic scenarios
- Users opt in by adding the source generator package
Title: Add source generator to eliminate reflection-based dispatch and enable NativeAOT
Labels:
enhancement,mediatorBody:
Problem
Mediator.csuses runtime reflection withConcurrentDictionary<Type, MethodInfo>caches to dispatch commands, queries, and stream queries when the concrete type is only known at runtime (the single-generic-parameter overloads likeSendCommandAsync<TResult>(ICommand<TResult>)). This has several drawbacks:MakeGenericMethodon first call per typenew object[] { command, cancellationToken }on every reflected dispatchProposed Solution
Create a
Cortex.Mediator.SourceGeneratorpackage that uses Roslyn incremental source generators to:ICommand<T>,IQuery<T>,IStreamQuery<T>, andINotificationimplementationsGeneratedMediatorclass with strongly-typed dispatch (no reflection)AddCortexMediator()) that registers all handlers without Scrutor[assembly: MediatorAssembly]or similar attribute to control scanning scopeBenefits
Mediatorby martinothamar)Scope
Cortex.Mediator.SourceGeneratorMediatorclass should remain as a fallback for dynamic scenarios