Click here to Skip to main content
15,867,308 members
Articles / Programming Languages / C# 4.0

Dynamic Send Activity in WF4

Rate me:
Please Sign up or sign in to vote.
4.43/5 (6 votes)
30 May 2010CPOL14 min read 62.1K   988   29   10
This article describes a design, implementation and usage of the custom Dynamic Send Activity in the .Net WF4 Technology.

 

Contents

 

Features

  • Binding scope for Send activity
  • Dynamically change binding
  • Dynamically inserting message headers
  • Built-in QueueMessageSend activity
  • .Net 4 Technologies

 

Introduction

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:

Image 1

 

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:

Image 2

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:

Image 3

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:

Image 4

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.

 

Concept and Design

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 
{
  // Inputs (Binding, Headers)
  
  // Body (Send Activity)
  
  // Targeting (runtime updating a Body)
}

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)
    {
        // inject headers
    }

    if (this.Binding != null)
    {
        dynamic dynBinding = this.Binding.Get(context);

        // update binding
    }

    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.

Image 5

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:

Image 6

and the following pictures show an example of the Headers property:

Image 7

Image 8

 

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:

Image 9

 

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:

Image 10

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.

 

Usage and Test

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:

 

Image 11

 

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:

Image 12

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:

 

Image 13

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:

Image 14

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.

 

Image 15

 

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:

Image 16

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.

Image 17

Step 2.

Launch the WorkflowFormsDesignerTester program. The workflow designer will loaded automatically the last workflow. You should have the following screen:

Image 18

The following pictures show value of the properties, the first one is for header and the other one for Send activity:

Image 19

Image 20

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.

Image 21

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:

Image 22

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.

Image 23

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:

Image 24

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

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)
{
  // inject headers
  if (this.Headers != null)
  {
    context.Properties.Add("MessageInspector", new SendMessageInspector() 
      { MessageHeaders = this.Headers.Get(context) });
  }

  // update binding
  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.

 

 

Conclusion

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.

 

 

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior)
United States United States
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
QuestionAnother MS Gap Filled Pin
joyceda17-Mar-14 0:38
joyceda17-Mar-14 0:38 
GeneralMy vote of 4 Pin
Bob Stanneveld28-Oct-11 7:30
Bob Stanneveld28-Oct-11 7:30 
GeneralRe: My vote of 4 Pin
Roman Kiss30-Oct-11 8:26
Roman Kiss30-Oct-11 8:26 
QuestionEndpointBehaviour Pin
Bob Stanneveld28-Oct-11 2:33
Bob Stanneveld28-Oct-11 2:33 
QuestionSend Scope with dyamic KnownTypes Pin
DelMan20066-Aug-11 0:45
DelMan20066-Aug-11 0:45 
Hi tanks for usefull post
I want to add KnownTypes dynamically to Send activity.
So i design KnownTypeScope activity as you describe
it has List<activity> Activitiese as public member and in Execute method before calling ScheduleActivity method, i add some types as KnownType For Sends activitiese that exists in Activitiese.

CSS
But after executing send activity this exception occured:
"There was an error while trying to serialize parameter http://tempuri.org/:customer.
the inner exception message was 'Type Test AppCommon.Customer' With Contract Name
... is not expected. Consider using a DataContractResolver or add any Types not known
statically to the list of known types"

but i added known types before runnig KnownTypeScopeActivity Activitiese in "Execute Method"


on executing of KnownTypeScopeActivity when i see content of Send activitese knowntypes after add knowntypes to Sends activitiese so every thing is ok, there are some type in Send Activitiese KnownType.

C#
here is my code:
[Designer(typeof(KnownTypeScopeActivitieseDesigner))]
Public class KnownTypeScpoe : NativeActivity
{
  Public List<Activity> Activities { get; set; }
  public KnownTypeScope()
  {
    Activities = new List<Activities>();
  }

  public override void Execute(NativeContext)
  {
   //Get Send Activitiese from Activities
    Send CurrentSend = GetSendActivitiese()[0];
    CurrentSend.KnownTypes.add(typeof(Customer));
    context.ScheduleActivity(Activities[0]);

  }
}


I insert a 'send and Receive Reply' activity in to KnownTypeScope and run it, so context.ScheduleActivity(Activities[0]) is for test.
Tank you

modified on Tuesday, August 9, 2011 1:21 AM

GeneralDoesn't work out of the box Pin
Lee Humphries14-Nov-10 14:24
professionalLee Humphries14-Nov-10 14:24 
GeneralRe: Doesn't work out of the box Pin
Roman Kiss14-Nov-10 17:44
Roman Kiss14-Nov-10 17:44 
GeneralRe: Doesn't work out of the box Pin
Lee Humphries15-Nov-10 16:34
professionalLee Humphries15-Nov-10 16:34 
QuestionCan Send Activity call the webservice Pin
zhuqil30-May-10 23:35
zhuqil30-May-10 23:35 
AnswerRe: Can Send Activity call the webservice Pin
Roman Kiss31-May-10 6:20
Roman Kiss31-May-10 6:20 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.