Migrating off IDirectLocalDispatcher
IDirectLocalDispatcher and both DispatchLocalAsync overloads have been removed from the public
surface. If your build broke on either name, this page is the whole migration.
What to call instead
IDispatcher.DispatchAsync. No cast, no opt-in, no configuration:
// Before
if (dispatcher is IDirectLocalDispatcher direct)
{
var response = await direct.DispatchLocalAsync<GetOrderQuery, OrderDto>(query, ct);
}
// After
var result = await dispatcher.DispatchAsync<GetOrderQuery, OrderDto>(query, ct);
var response = result.ReturnValue;
One behavioral difference to handle: DispatchAsync reports failure through the returned
IMessageResult, where DispatchLocalAsync threw. A failure you previously caught is now a result
you inspect -- see Results and Errors.
Why it was removed
DispatchLocalAsync bypassed middleware unconditionally. Its eligibility check asked whether the
handler invoker was the concrete one and whether local retries were off -- it never asked whether
you had registered any middleware. So validation, authorization and telemetry that you had
configured were silently skipped, with no diagnostic, on an injectable public API. Gating the method
on middleware would only have made every call fall through to the standard path, so the method is
gone instead.
Are you giving up performance?
No -- you gain a little. The ultra-local fast path is an internal optimization, not an API, and it
is unchanged: when a local action has no middleware applicable to its type, DispatchAsync takes
the short path automatically, exactly as it did before. The explicit method was never the faster of
the two. It measured slower than the standard call while doing strictly less work -- it created no
message context and returned no IMessageResult -- so removing it makes the public call faster,
not slower.
Tuning the fast path
The fast path is still configurable through DispatchOptions.CrossCutting.Performance. It is used
only when the message can stay local; otherwise Dispatch falls back to the full pipeline
automatically. Common fallback triggers:
- middleware applies to that message type
- a non-local routing decision
- a local retry mode that requires the richer execution path
- operations that require full context-bound semantics
Context initialization on the fast path
| Context field/state | Behavior |
|---|---|
context.Message | Set |
context.CorrelationId (when correlation enabled and missing) | Generated |
context.CausationId (when missing and correlation present) | Set from correlation |
context.MessageType (when missing) | Populated |
Existing values are preserved -- Dispatch only fills missing values. Correlation is governed by
Features.EnableCorrelation; when it is on, the fast path initializes it like any other path.
EmitDirectLocalResultMetadata
false(default): minimal success result shape on the local fast path.true: include full result metadata on local fast-path success.
See Core Configuration for the registration snippet and the
appsettings.json equivalent.