Feature Collection & Payload Bag
Every MessageContext carries two extension mechanisms for sharing data across middleware without coupling to a specific transport:
- Feature collection (
IFeatureCollection): a type-safe dictionary of named capabilities, accessed viacontext.Features. - Payload bag (
SetPayload/TryGetPayload): a type-keyed dictionary for transport-specific objects, accessed directly on the context.
Feature collection
Section titled “Feature collection”Reading a feature
Section titled “Reading a feature”var feature = context.Features.Get<IResponseFeature>();if (feature is not null){ await feature.RespondAsync(result);}Get<T> returns null if the feature is not present. Features are optional by design; middleware that reads them should always null-check before using them.
Writing a feature
Section titled “Writing a feature”context.Features.Set<IMyCustomFeature>(new MyCustomFeature(value));Later middleware in the same pipeline can read it. Features are scoped to the lifetime of the context.
Built-in features
Section titled “Built-in features”IResponseFeature
Section titled “IResponseFeature”For request-response patterns via the mediator. Carries the response back to the caller. Not present for broker consumers.
| Member | Description |
|---|---|
HasResponded | Whether a response has already been set |
RespondAsync<TResponse> | Sets the response and completes the request |
Payload bag
Section titled “Payload bag”The payload bag stores transport-specific objects keyed by their CLR type. Unlike features (which use interface keys), the payload bag stores concrete types directly. Use it when you want to pass a fully-typed object that does not need an interface contract.
Writing a payload
Section titled “Writing a payload”context.SetPayload(myObject);Reading a payload
Section titled “Reading a payload”var payload = context.TryGetPayload<MyType>();if (payload is not null){ // use it}Built-in payloads
Section titled “Built-in payloads”KafkaTransportContext<TKey>
Section titled “KafkaTransportContext<TKey>”On the consume side, KafkaTransportContext<TKey> is set as the TransportContext on ConsumeContext<T>. It carries all Kafka-specific metadata plus the deserialized message key:
if (context.TransportContext is KafkaTransportContext<string> kafka){ logger.LogDebug("Key: {Key}, Topic: {Topic}, Partition: {Partition}, Offset: {Offset}", kafka.Key, kafka.Topic, kafka.Partition, kafka.Offset);}On the produce side, the same type is stored as a payload on SendContext<T> so the terminal producer can extract the key:
var kafka = context.TryGetPayload<KafkaTransportContext<string>>();// kafka.Key contains the message keyContext properties
Section titled “Context properties”Most metadata middleware needs is available directly on the context, without going through features or payloads. This table is the authoritative reference; the consumers page links here rather than maintaining its own copy.
| Property | Location | Description |
|---|---|---|
DestinationAddress | MessageContext | Transport URI (e.g. kafka://broker:9092/orders) |
SourceAddress | MessageContext | Sender address, propagated via headers |
Message | MessageContext<T> | The typed message payload |
MessageId | MessageContext | Unique message identifier |
Timestamp | MessageContext | When the message was created or received |
Headers | ConsumeContext<T> | Message headers (delegated from TransportContext) |
RetryAttempt | ConsumeContext<T> | Current retry count (0 on first attempt) |
Transaction | ConsumeContext<T> | Active transaction context, if any |
TransportContext | ConsumeContext<T> | Full transport metadata: raw bytes, headers, provider ID |
Extracting transport info from URIs
Section titled “Extracting transport info from URIs”Middleware can derive transport-agnostic information from DestinationAddress without knowing which transport is in use:
// Get the transport scheme (e.g. "kafka")var provider = EmitEndpointAddress.GetScheme(context.DestinationAddress);
// Get the entity name (e.g. topic name "orders")var destination = EmitEndpointAddress.GetEntityName(context.DestinationAddress);