Skip to content

Routing Slip Builder

Instead of directly implementing the RoutingSlip message type, developers are encouraged to use a RoutingSlipBuilder to construct the routing slip. The RoutingSlipBuilder simplifies the process by providing methods to add activities (and their arguments), activity logs, and variables to the routing slip. For example, to create a routing slip with two activities and an additional variable, a developer might write:

var builder = new RoutingSlipBuilder(NewId.NextGuid());
var routingSlip = builder.Build();
var builder = new RoutingSlipBuilder(NewId.NextGuid());
builder.AddActivity("DownloadImage", new Uri("rabbitmq://localhost/execute_downloadimage"),
new
{
ImageUri = new Uri("https://images.google.com/someImage.jpg")
});
builder.AddActivity("FilterImage", new Uri("rabbitmq://localhost/execute_filterimage"));
builder.AddVariable("WorkPath", @"\dfs\work");
var routingSlip = builder.Build();

Each activity requires a name for display purposes and a URI specifying the execution address. The execution address is where the routing slip should be sent to execute the activity. For each activity, arguments can be specified that are stored and presented to the activity via the activity arguments interface type specified by the first argument of the IActivity interface. The activities added to the routing slip are combined into an Itinerary, which is the list of activities to be executed and stored in the routing slip.

Managing the inventory of available activities, as well as their names and execution addresses, is the responsibility of the application and is not part of the MassTransit Courier. Since activities are application-specific, and the business logic to determine which activities to execute and in what order is part of the application domain, the details are left to the application developer.

Each activity declares an activity argument type, which must be an interface. When the routing slip is received by an activity host, the argument type is used to read data from the routing slip and deliver it to the activity.

The argument properties are mapped, by name, to the argument type from the routing slip using:

  • Explicitly declared arguments, added to the itinerary with the activity
  • Implicitly mapped arguments, added as variables to the routing slip

To specify an explicit activity argument, specify the argument value while adding the activity using the routing slip builder.

var builder = new RoutingSlipBuilder(NewId.NextGuid());
builder.AddActivity("DownloadImage", new Uri("rabbitmq://localhost/execute_downloadimage"), new
{
ImageUri = new Uri("https://images.google.com/someImage.jpg")
});
ParameterNotes
nameThis is a human readable label for the activity
addressThe address of the endpoint where the activity is listening. Short Addresses are also supported.
argsvalues to be bound to the Args type defined in this activity, not shared

To specify an implicit activity argument, add a variable to the routing slip with the same name/type as the activity argument.

var builder = new RoutingSlipBuilder(NewId.NextGuid());
builder.AddActivity("DownloadImage", new Uri("rabbitmq://localhost/execute_downloadimage"));
builder.AddVariable("ImageUri", "https://images.google.com/someImage.jpg");

These values are shared across activities. This is a great way to pass data from one activity to the next.

ParameterNotes
keyThe key to retrieve it from later
valueThe value

If an activity argument is not specified when the routing slip is created, it may be added by an activity that executes prior to the activity that requires the argument. For instance, if the DownloadImage activity stored the image in a local cache, that address could be added and used by another activity to access the cached image.

First, the routing slip would be built without the argument value.

var builder = new RoutingSlipBuilder(NewId.NextGuid());
builder.AddActivity("DownloadImage", new Uri("rabbitmq://localhost/execute_downloadimage"));
builder.AddActivity("ProcessImage", new Uri("rabbitmq://localhost/execute_processimage"));
builder.AddVariable("ImageUri", "https://images.google.com/someImage.jpg");

Then, the first activity would add the variable to the routing slip on completion.

async Task<ExecutionResult> Execute(ExecuteContext<DownloadImageArguments> context)
{
...
return context.CompletedWithVariables(new { ImagePath = ...});
}

The process image activity would then use that variable as an argument value.

async Task<ExecutionResult> Execute(ExecuteContext<ProcessImageArguments> context)
{
var path = context.Arguments.ImagePath;
}

By default, routing slip events are published — which means that any subscribed consumers will receive the events. While this is useful getting started, it can quickly get out of control as applications grow and multiple unrelated routing slips are used. To handle this, subscriptions can be used to change the way routing slip events are produced.

Subscriptions are added to the routing slip at the time it is built using the RoutingSlipBuilder.

builder.AddSubscription(new Uri("rabbitmq://localhost/log-events"),
RoutingSlipEvents.All);
ParameterNotes
addressWhere should routing slip events be sent
eventsRoutingSlipEvents is a flag enum for selecting desired events

This subscription would send all routing slip events to the specified endpoint. If the application only wants specific events, those events can be selected by specifying their enumeration values. For example, to only get the RoutingSlipCompleted and RoutingSlipFaulted events, the following code would be used.

builder.AddSubscription(new Uri("rabbitmq://localhost/log-events"),
RoutingSlipEvents.Completed | RoutingSlipEvents.Faulted);

It is also possible to tweak the content of the events to cut down on message size. For instance, by default, the RoutingSlipCompleted event includes the variables from the routing slip. If the variables contained a large document, that document would be copied to the event. Eliminating the variables from the event would reduce the message size, thereby reducing the traffic on the message broker. To specify the contents of a routing slip event subscription, an additional argument is specified.

builder.AddSubscription(new Uri("rabbitmq://localhost/log-events"),
RoutingSlipEvents.Completed, RoutingSlipEventContents.None);

This would send the RoutingSlipCompleted event to the endpoint, without any of the variables be included (only the main properties of the event would be present).

Once a subscription is added to a routing slip, events are no longer published — they are only sent to the addresses specified in the subscriptions. However, multiple subscriptions can be specified— the endpoints just need to be known at the time the routing slip is built.

It is also possible to specify a subscription with a custom event, a message that is created by the application developer. This makes it possible to create your own event types and publish them in response to routing slip events occurring. And this includes having the full context of a regular endpoint Send so that any headers or context settings can be applied.

To create a custom event subscription, use the overload shown below.

// first, define the event type in your assembly
public record OrderProcessingCompleted
{
public Guid TrackingNumber { get; init; }
public DateTime Timestamp { get; init; }
public string OrderId { get; init; }
public string OrderApproval { get; init; }
}
// then, add the subscription with the custom properties
builder.AddSubscription(new Uri("rabbitmq://localhost/order-events"),
RoutingSlipEvents.Completed,
x => x.Send<OrderProcessingCompleted>(new
{
OrderId = "BFG-9000",
OrderApproval = "ComeGetSome"
}));

In the message contract above, there are four properties, but only two of them are specified. By default, the base RoutingSlipCompleted event is created, and then the content of that event is merged into the message created in the subscription. This ensures that the dynamic values, such as the TrackingNumber and the Timestamp, which are present in the default event, are available in the custom event.

Custom events can also select with contents are merged with the custom event, using an additional method overload.