Contents
- Binding scope for Send activity
- Dynamically change binding
- Dynamically inserting message headers
- Built-in QueueMessageSend activity
- .Net 4 Technologies
This article will be shorter than my previously ones. It is focusing on extensibility of the Send
activity in the WF4 paradigm. The Send activity represents a connectivity primitive for consuming services in the specific exchange message pattern (MEP). Based on the setup properties, the WCF channel creates a message that is sent out of the workflow domain. The following screen snippet shows the grid of its properties:
The usage of the Send Activity is very straightforward, basically the message contract and AddressBindingContract (ABC)
properties are setup during the design time and executed in the runtime. There are very limited ways for runtime flexibility in the Send
activity, for instance, we can use a generic untyped message contract for creating a generic proxy (see more details about this message mediation in my previous article here) or we can select a client configuration in the config file, etc.
As you can see in the above picture, the Endpoint
property must be declared during the design time, there is no way to declaratively select a binding dynamically during the runtime, for instance based on the rule repository. Another runtime requirement for Send activity is adding an application header into the outgoing message during the runtime and based on the repository resource.
In other words, we need a dynamic Send activity for controlling its ABC properties. The following picture shows an example of this custom activity for BindingScope around the Send activity:
In this custom activity, the Binding is an ExpressionTextBox (ETB) property with a dynamic
type, therefore we can use XElement
, String
or Binding
classes for declaring a binding explicitly or implicitly via variables. It seems, based on the above picture, the Send activity has its own binding section declared in the xaml resource instead of the config file.
To hide same binding complexity declared in the binding section (see the above picture with a netMsmqBinding
), we can have a prebuilt custom binding scope of the Send activity for a specific binding. The following picture shows a custom SendQueueMessage activity for MSMQ messaging:
As you can see, the above Dynamic Send Activity will create a netMsmqBinding
binding in the runtime and assign it to the Send.Endpoint
property.
The BindingScope
represents a binding wrapper around the Send
activity. The following screen snippet shows this scope for SendQueueMessage
binding:
Note, the Send activity doesn't require any changes, therefore we can use this scope for Send activity declared in the designed workflow, just dropping the Send activity into the scope area and the binding scope will overwrite its hardcoded (combobox selected) Endpoint property.
OK, let's get started with Concept and Design of the Dynamic Send Activity.
I am assuming you have some working experience or understand features of the WF4 Send Activity.
The concept and design of the Dynamic Send Activity is based on the feature of System.Activities.NativeActivity.
Custom activity derived from the NativeActivity will have all access to the runtime workflow engine model. The following code snippet shows this basic concept:
[ContentProperty("Body")]
[DesignTimeVisible(false)]
public sealed class BindingScope : NativeActivity
{
}
The NativeActivityContext is passed during the execution of the activity which can be used to schedule other activities (in our design it is a Send activity) using the ScheduleActivity method. This is a very powerful feature of the WF4 model, allowing us to pre-process an activity before its executing during the runtime.
The following code snippet shows this override method where this.Body
is our scoped Send activity. Before passing this activity for execution, we can update its binding and registering a callback function for injecting a message headers:
protected override void Execute(NativeActivityContext context)
{
if (this.Headers != null)
{
}
if (this.Binding != null)
{
dynamic dynBinding = this.Binding.Get(context);
}
context.ScheduleActivity(this.Body);
}
The second important override method is a CacheMetadata
. This method validates our Send activity and setup of a dummy Endpoint:
protected override void CacheMetadata(NativeActivityMetadata metadata)
{
if (this.Body == null)
metadata.AddValidationError("Missing a Send Activity in the scope");
if (this.Body != null && this.Body.Endpoint == null)
this.Body.Endpoint = new Endpoint()
{
Binding = new BasicHttpBinding(),
AddressUri = new Uri("http://localhost/Fake")
};
metadata.AddChild(Body);
}
As you can see, the concept of Dynamic Send Activity is very straightforward, using an extensibility of WF4 paradigm. In this article, I am only demonstrating the scope of the binding and injecting the message headers, but it can be used for any property, for instance, KnownTypes
. The scope activity can have a simple designer or more sophisticated one, such as access to the repository for binding templates, headers, etc.
Let's describe all custom Dynamic Send Activities included in this article solution.
SendScope
The SendScope is a basic custom activity for scoping a Send Activity with a minimum designer.
There are two properties in this scope, the first one is for declaring a binding and the other one is for headers. As I mentioned earlier, the ETB property is defined as dynamic type, therefore the binding can be declared like it is shown in the following picture:
and the following pictures show an example of the Headers property:
The usage of this custom activity is very straightforward like a Send activity. In addition, the binding and headers can be declared dynamically by xaml.
BindingScope
The BindingScope is a custom activity like SendScope activity with designer for binding section. The following picture shows this design:
The Binding is a ETB property with multi lines text, which allows us to edit a xml text directly from the designer surface. The binding has the same xml formatted text like binding section in the config file. Note, that this article publishes a simple designer without the editor dialog, access to the repository for binding template, etc.
SendQueueMessage
The SendQueueMessage
is a custom scope activity around the Send activity for specific binding such as NetMsmqBinding
. The following picture shows its designer and properties:
The SendQueueMessage
allows sending a transactional message with custom headers to the private queue located on the specific machine. The message can be controlled by TimeToLive
and DeadLetterQueue
properties. The usage of this custom activity is very powerful. Let's dig deeper about this message exchange pattern in the following paragraph.
MSMQ 4.0 is a great Microsoft Technology. It is free (part of the operation system) and very reliable messaging mechanism for express, reliable or transactional messages between the local or remote queues. WCF has built-in a net.msmq
channel for abstracting access to the MSMQ paradigm and virtualizing a connectivity between the service and its consumer. Before the WCF/WF Technology, I built this abstraction and virtualization for custom Remoting channel long time ago (see my article here posted when .Net 1.0 has been release) and used it in many projects to encapsulate a business layer from the physical communication channels. This concept allows for migrating a legacy application very smoothly to the WCF/WF Technology.
The end of this story is decomposition of the application into the small business actions (processes) and their composition using a MSMQ Technology enables to manage and scale our application in the loosely coupled event driven manner.
The following picture shows a typical scenario for decoupling process into the Manager and Workers processes. The Manager walks through the set of rules and generates messages for workers via MSMQ:
The Worker Service can be controlled by setting suitable service throttling configuration based on the hosting capability, etc. SendQueueMessage activity can be used for sending a transactional message to the worker:
The above picture shows a message (with a custom header) in the queue Search
that must be received within 30 seconds. There is no DeadLetterQueue (DLQ) specified, therefore the undelivered message will be lost.
Let's make a more realistic scenario, for example, the worker service for some reason failed and its work must be repeated again. WE can have more solutions for this requirements such as crating a custom Retry activity, etc. but, by using the MSMQ 4.0 features, it can be achieved declaratively just by reconfiguring a SendQueueMessage. This magic feature is based on the DeadLetterQueue without a listener/receiver.
The following picture shows this scenario:
As you can see, we have an additional queue (WaitQueue) without the listener. The message retrying trick is based on moving a message from the waiting queue to the dead letter queue after expiring a TimeToLive time. The following picture shows properties of the SendQueueMessage for worker service:
Now, I can have a capability of the dynamic Send activity using a custom header in the message. In our example of retrying a worker service, we can dynamically update a message header for purpose of the retrying process.
Note, that MSMQ 4.0 doesn't support movement a message to the DeadLetterQueue across the machine. It will be nice to have this feature in the cluster which will be a very simple scale out for our process execution. For now, we have to consider that both queues must be on the same machine including the sender. But wait a moment, we have a WCF4 magic service for this workaround, which I strongly recommend to use in your enterprise composite application. My recent article describes in detail more about this service such as Routing Service here.
The following picture shows an example of the composite application decoupled into small business oriented workflow services and composition using a Routing Service and MSMQ Technology.
Based on the FilterTable
contents, the messages can be routed within the same machine or across the balanced cluster using the http channel. The workflow service will send a message via the http channel with a cluster address to the Router service. The Router service (may be another node, based on balancer setup) will forward a message to the local queue and etc. As you can see, we can have a runtime manageable model of composite application via metadata stored in the repository (workflow services, FilterTable, etc.)
One more thing, when we use a DeadLetterQueue such as a retrieving a message from this queue, the service address (listener uri) is declared for the physical queue from where we want to receive the messages. Everything is going right if the message has been sent from its consumer (client). But, in the case of the DLQ scenario, we have a different situation, because the message has been moved from another queue (generated by another client, contract, etc.). To receive this message by DLQ service, we need to mediate an AddressFilter of the endpoint. We can declare service behavior for [ServiceBehavior(AddressFilterMode=AddressFilterMode.Any)]
or creating a custom endpointBehavior for specific AddressFilter or we can use a Routing Service for this magic routing like is done in the following Test 2.
That's all for usage, let's show some test cases to see how a Dynamic Send activity is working.
Test
The Dynamic Send Activity has been built in the following solution:
As you can see, there are few projects designed and implemented for testing support such as re-hosted workflow designer, self-hosted console program and self-hosted console program for generic service. I will skip the description of each project as you can find more information about this tester in my privious article here.
Ok, let's start 123 test. Please, create a non-transactional queue Test,
transactional queues Search
and Wait
and set a full permissions for all queues.
TEST 1 - Simple test
Step 1.
Launch the WorkflowConsoleServiceTester. This program is hosting a service for receiving messages from the Test queue. The received message is displayed on the screen.
Step 2.
Launch the WorkflowFormsDesignerTester program. The workflow designer will loaded automatically the last workflow. You should have the following screen:
The following pictures show value of the properties, the first one is for header and the other one for Send activity:
OK, now we are ready to create a test, such as sending a message with header do the Test queue and the message receiving by console program.
Step 3.
Press the button Run
to host and run our test workflow in the own hosted console program. If everything is going fine, you should see the following responses on the console programs. The first one is shoving a received message from the queue and the other one shows workflow processing.
Once we know that the test passed, we can go back to the workflow designer and modify its workflow for more complex test using a DeadLetterQueue, adding BindingScope activity, etc.
TEST 2 - Finally test
This test will demonstrate all custom Dynamic Send Activities to send transactional and non-transactional queued messages via router to the service tester. Before the test, we have to change xaml resource in the workflow designer. As you can see in the solution package, there is a folder Examples with two files.
Step 1
Copy the contents of the Test2.xaml file into the clipboard.
Step 2
Paste the clipboard in the TreeView of the Workflow Designer. To do this, move the horizontal splitter up and delete the node Activity. The following screen snippet shows these controls:
Once we have our Test2 contents in the TreeView, please press the Load button for loading xaml into the designer workspace.
Step 3.
The following picture shows our Test2 in the designer area.
As you can see, the above test has three major scoops. The first one will send the queue message to the Wait
queue and after elapsed TimeToLive time, the message will be moved to the Search
queue. The second scope demonstrates a binding scope for non-transactional queue message. The address of the Endpoint is setup in the Send activity. The last scope demonstrates sending a transactional message where a binding is declared in the ETB property Binding using a NetMsmqBinding
class.
In this test, I also demonstrate a routing feature as an integration service between the MSMQ and service tester. This plumbing is done declaratively in the following config file:
I do recommend using a routing service in your infrastructure as it is a very powerful architectural component. In our above test, we have two inbound endpoints and one outbound to the service tester. Based on the filterTable, the inbound message can be routed to the outbound endpoint. In this example, all inbound messages will be forwarded to the service tester.
Step 4
Press the button Run to execute a workflow. You should see additional three messages on the console program received by the service tester.
That's all for the usage and testing Dynamically Send activity. Let's look at its implementation.
Implementation of the Dynamic Send Activity is based on deriving a custom scope activity from the NativeActivity for having
a full access to the workflow runtime model. Creating a scope activity for Send activity will enable us to pass arguments during the design time and use them in the runtime for programmatically massaging a Send activity.
Let's look at how the BindingScope is implemented.
First of all, the following code snippet shows a factory class for templatetizing scope with a Send activity:
public class BindingScopeFactory : IActivityTemplateFactory
{
public Activity Create(DependencyObject target)
{
return new BindingScope()
{
DisplayName = "BindingScope",
Body = new Send()
{
Action = "*",
OperationName = "ProcessMessage",
ServiceContractName = "IGenericContract",
Endpoint = new Endpoint()
{
Binding = new BasicHttpBinding()
}
}
};
}
}
The above template setup the Send activity for untyped message and basicHttpBinding, therefore we don't have any validation problem. Note, this is a default setup and it can be overwriten by BindingScope during the runtime time, like it is shows in the following code snippet:
protected override void Execute(NativeActivityContext context)
{
if (this.Headers != null)
{
context.Properties.Add("MessageInspector", new SendMessageInspector()
{ MessageHeaders = this.Headers.Get(context) });
}
if (this.Binding != null)
{
dynamic dynBinding = this.Binding.Get(context);
if (dynBinding is XElement || dynBinding is string)
{
XElement bindingElement = dynBinding is XElement ?
dynBinding : XElement.Parse(dynBinding);
var bs = LibHelper.DeserializeSection<BindingsSection>(
string.Concat("<bindings>", bindingElement.ToString(), "</bindings>"));
var bce = bs.BindingCollections.Find(b=>b.BindingName==bindingElement.Name.LocalName);
Binding binding = Activator.CreateInstance(bce.BindingType) as Binding;
if (bce.ConfiguredBindings.Count == 1)
bce.ConfiguredBindings[0].ApplyConfiguration(binding);
this.Body.Endpoint.Binding = binding;
}
else
{
this.Body.Endpoint.Binding = dynBinding;
}
}
context.ScheduleActivity(Body, OnFaulted);
}
As you can see, the binding is a dynamic type such as XElement
, String
or Binding
object. In the case of XElement or String we have to deserialize xml formatted text into the BindingSection
type, then we are creating a Binding object and applying its configuration. Once we have a binding object, it can be assigned to the Endpoint. It's very straightforward logic once we have a custom LibHelper.DeserializeSection<T> function.
WF4 has built-in plumbing support for accessing an OperationContext
before sending a message and after receiving a message. The concept is based on the callbacks from the WCF MessageInspector. In our implementation, the ISendMessageCallback
is implemented. The following code snippet shows this callback for adding a header into the OutgoingMessageHeaders
collection:
void ISendMessageCallback.OnSendMessage(OperationContext context)
{
if (this.MessageHeaders is List<MessageHeader>)
{
foreach (MessageHeader header in this.MessageHeaders)
{
context.OutgoingMessageHeaders.Add(header);
}
}
else if (this.MessageHeaders is MessageHeader)
{
context.OutgoingMessageHeaders.Add(this.MessageHeaders);
}
else if (this.MessageHeaders is XElement)
{
if (string.Compare(this.MessageHeaders.Name.LocalName, "Header", true)==0)
{
foreach (var eh in this.MessageHeaders.Elements())
{
context.OutgoingMessageHeaders.Add(this.CreateHeader(eh));
}
}
else
{
context.OutgoingMessageHeaders.Add(this.CreateHeader(MessageHeaders));
}
}
else
throw new Exception("Wrong MessageHeader type");
}
Note, that the callback object must be registered (added) in the NativeActivityContect.Properties collection for its invoking.
In conclusion, this article described a custom scope design pattern around the Send activity, but it can be used for any sequence, for instance, state machine, nested workflow, remote workflow, etc. Using an abstract class NativeActivity
for custom activity allows us to update its child properties programmatically. In addition, I showed you how easily routing service can be used for integration process instead of building a custom service behavior for the endpoint address filtering.