Content-Based Routing
Content-based routing lets you inspect an incoming message and dispatch it to a different consumer depending on what it contains. This is useful when a single topic carries multiple logical event types, or when you want to shard work across handlers based on a field value.
AddRouter
Call AddRouter on the consumer group instead of AddConsumer:
topic.ConsumerGroup("pizza-kitchen", group =>{ group.AddRouter( identifier: "crust-type-router", selector: ctx => ctx.Message.CrustType, configure: router => { router.Route<ThinCrustHandler>("thin"); router.Route<DeepDishHandler>("deep-dish"); router.Route<GlutenFreeHandler>("gluten-free"); });});The selector receives the full ConsumeContext<TValue> and returns the route key. That key is matched against the registered routes, and the matching consumer is resolved from the DI container and invoked.
Route key types
The route key type is inferred from the selector return type. It must be notnull. Common patterns:
// String discriminator fieldselector: ctx => ctx.Message.EventType
// Enum valueselector: ctx => ctx.Message.OrderKind
// Derived valueselector: ctx => ctx.Message.CorrelationId.StartsWith("B2B") ? "b2b" : "b2c"Per-route middleware
Each route can have its own middleware layer, wrapping only that handler:
router.Route<DeepDishHandler>("deep-dish", route =>{ route.Use<PriorityTracingMiddleware>();});Unmatched messages
If the selector returns a key with no registered route, an UnmatchedRouteException is thrown. Configure how this is handled via the group’s OnError policy:
group.OnError(err =>{ err.WhenRouteUnmatched(action => action.Discard()); err.Default(action => action .Retry(3, Backoff.Fixed(TimeSpan.FromSeconds(1))) .DeadLetter());});If the selector returns null, the message is silently skipped.
Accessing the matched route in middleware
After routing, the matched route key is tagged on the current Activity when tracing is enabled:
var routeKey = Activity.Current?.GetTagItem("emit.route.key")?.ToString();The consumer type name and kind are baked into the tracing middleware at build time and do not need to be read manually.
Router vs. multiple consumer groups
Use a router when all messages arrive on the same topic and you need to dispatch them differently based on content. Use multiple consumer groups when you need independent Kafka offsets: different replay positions, separate lag tracking, or independent scaling.