Click here to Skip to main content
15,867,308 members
Articles / Desktop Programming / XAML

Using WF4 WorkflowInvoker

Rate me:
Please Sign up or sign in to vote.
5.00/5 (16 votes)
1 Sep 2009CPOL13 min read 92.9K   1.2K   42   9
This article describes a design, implementation and usage of the custom service operation invoker for invoking a xaml workflow. It is based on the upcoming Microsoft .NET 4 Technology.

 

NOTE: This article has been written using the .NET Framework 4 Beta 1 version.

 

 

Contents

 

Features

  • Loosely coupled design pattern
  • Custom  Operation Invoker for WF4 workflow
  • Declaratively business encapsulation for product flexibility
  • Xaml Method vs Clr Method
  • Incremental (operation by operation) way to metadata driven application
  • Logical Centralized Workflows (xaml methods) in the Repository
  • IIS/WAS, self-hosting, etc.
  • Flexible location of the xaml resource
  • WCF and WF4 Technologies

 

Introduction

Microsoft recently released Visual Studio 2010 and .Net Framework 4 Beta 1. The Windows Workflow Foundation (WF4) in .NET 4 Beta 1 introduced a significant amount of changes comparing to the current technologies such as .NET 3.0 and 3.5 versions. Let me explain a little bit why those changes were necessary.

The first time WF was introduced at the PDC2005 as a separate windows foundation technology. It was based on its own programming model, hosting and private communication layer to/from host application. This concept enables the usage WF Technology side by side with other technologies.

Therefore, in that time we were introduces to great new technologies for communications and workflow driven applications. The bad news was, that they couldn't work seamlessly together. They were logically and physically isolated from each other. Their runtime models, troubleshooting, monitoring, etc, was different. For more details, please visit my articles published on the codeproject on how to integrate these technologies together.

Each model (WCF and WF) was based on its own metadata and configuration and they required more plumbing coding. The next version, such is .Net 3.5 introduced a common runtime model for integration of the WCF and WF programming models. It introduced a new paradigm such as WorkflowServices.

The WorkflowServices enable declaratively way to go "behind the endpoint". In other words, based on the metadata (xoml),  the message and service can be mediated and orchestrated in the loosely decoupled manner. In my opinion, the WorkflowService represents a quality different approach of the service - a manageable service, which can be logical centralized in the Repository and physical decentralized for its runtime projecting, for example on the cloud. Visit my article Manageable Service for more details about this strategy.

The end of this story, today we have a production technology (.Net 3.5) which integrated WCF + WF into one runtime programming model, but still each model has its own metadata such as configuration, xoml and rules. There is no common metadata model (stack) for its runtime projecting in the different hosting environments. This challenge is upcoming in the .Net Framework 4, where its first public Beta 1 has been introduced.

All this challenge is driven by Microsoft Strategy for Model-Driven Development, where "XAML stack" is playing a significant role for runtimes. We will manage only one logical stack for declaring components of the WPF, WCF, WF, etc. and their binding together. This feature was presented at the PDC2008, where we saw an example of a tinny  "xaml stack" embedded with communications and workflows in the Silverlight application. 

I hope you see the same lights (as me), why it was necessary to make a significant changes in the WF4 programming model. Using WF4 will dramatically simplify development process and increase productivity for declaratively programming. Please, visit this link for more details.

One of the "horse class" of the WF4 without the knowledge of the hosting, communications, eventing, etc. is the WorkflowInvoker. This article describes an extension of the service operation contract for decoupling an imperatively implementation of the service operation into the declaratively one. This approach allows moving a service operation by operation into the "workflow world", centralized operation mediation into the repository and manage them based on the needs/requirements. This approach gives your application a flexibility where changes can be done declaratively instead of coding and deploying new assemblies. This is a "baby step" declared only in the scope of the single operation into the metadata model-driven solution.

I am assuming you have some working experience with current WCF and WF Technologies. OK, let's get started.

 

Concept

The concept of processing a service operation declaratively instead of imperatively is very straightforward based on the WCF Framework extensibility. The following picture shows a WCF Framework with the place to invoke a service method:     

 

Image 1

 

As you can see, the OperationInvoker has a responsibility to invoke a CLR Method on the Service using a reflection mechanism. This class is derived from the System.ServiceModel.Dispatcher.IOperationInvoker which represents an extensibility point for customizing WCF Framework at the end of the message flowing in the channel stack. You can find many examples for custom operation invoker on Google such as caching, validation, etc.

Now, I am adding one more custom operation invoker, to process a service operation in declaratively way, in other words, custom operation for invoking a XAML Method. The following picture shows this concept:   

 

Image 2

 

As you can see by comparing the above pictures, the message is processing in full transparent manner without the knowledge of the service method implementation. Usage of invoking a xaml method in the service is straightforward like is shown in the following code snippet:

Image 3

The above code snippet is an example of the service Ping in the namespace Test. This service has three methods accessing and invoking in a different way . The first one such as Echo method is a regular clr method under the operation contract (WCF model), the second one is also under the WCF contract, but its implementation is declaratively using a xaml stack, (we can call it a XAML Method), and the third method is a private regular clr method.

Attributing a service operation by [OperationWorkflowInvokerAttribute], the WCF Framework will handle this operation like xaml method. By default, the method is declared in the xaml resource Test.Ping.Echo2.xaml file, located in the assembly folder, but it can be in the specific folder under different name. This article solution uses only File System for storing xaml resources, but with a small modification (adding a xaml loader), the xaml resources can be stored in the Repository. Note, that the clr implementation of this method will never be invoked, so the  NotImplementationException can be inserted inside the method body.

 

System.Activities.WorkflowInvoker

The System.Activities.WorkflowInvoker is a wrapper class around the generic WorkflowInstance to simplify initializing and running of a workflow instance synchronously in self-hosting workflow runtime. From the host application point of the view it is abstracted like "a workflow method" with the following signature:

Image 4

In other words, the WorkflowInvoker is saying: "Describe me by workflow (WorkflowElement)  what do you want, give me some inputs for it and I will process it and return back outputs in the synchronously manner".

Also, we can ask the WorkflowInvoker to deliver a result within the time limit, that's only external control for this method.

Note, that we cannot control a workflow processing in the WorkflowInvoker such as persist, cancel, abort, etc., therefore the workflow orchestration should consider that during its design and should be used for short-lived workflows only.

As I mentioned earlier, WF4 paradigm has been changed to the WF3/3.5. One of the changes is that the Activity has a signature and body similarly to the clr method. There are public arguments defined how the data flows in and out of an activity. The body of the activity represents chunk of execution logic called within the runtime context.  

Based on the above, we know that the plumbing of the WorkflowInvoker into the OperationInvoker requires two dictionaries, one for inputs and the other one for output arguments and WorkflowElement object which represents a root workflow graph.

Let's look at these pieces in my custom re-hosted WF4 Designer in the MMC for very simple workflow - Echo loopback:

Image 5

 

The root Workflow Activity declared two arguments such as text and _retval. Note, that the _retval is a hardcoded name of the return value. The execution body has only one child activity such as Assign and its responsibility is assigning a value of the argument text to the argument _retval. The xaml stack of this workflow is very simple like it is shown in the above picture.

That's the design time, when we are creating an operation method declaratively using the WF4 Designer which generates a xaml stack for runtime projecting.

The following code snippet shows an equivalent the above example using a coding way.

[OperationContract]
string Echo(string text)
{
   return text;
}

When you compare implementation of this simple Echo method using the coding and xaml declaration, the following question can be raised: Why do I need to learn this complexity for one line of coding? Where is the advantage of using the xaml method? and etc.

Well, these are the valid questions. But, there are more questions from the other side, such as, Do we need to change the method body?, What is the business logic complexity? Can we decouple a business login into the small reusable and well tested activities? Can we use 3rd party activities,  etc.?

OK, let's go back to the xaml resource created during the design time. This resource is a part of the deployment package. When process will start, we need to load this resource from the Repository (in this article it is a File System) and projecting it into the WorkflowElement graph object. This is only one time job and it can be completed in the  ApplyDispatchBehavior method during the service operation behavior extension, see the next implementation section.

 

Implementation

Implementation of the custom operation invoker for xaml method is divided into two classes based on the WCF Framework extensibility. The first one is derived from the System.Attribute and  System.ServiceModel.Description.IOperationBehavior  and its responsibility is to injected our custom operation invoker. The following code snippet shows a method with a pseudo body:

public void ApplyDispatchBehavior(OperationDescription description, 
  DispatchOperation dispatch)
{
    // load Xaml from repository (FileSystem, etc.)
    
    // create WorkflowElement (Dynamic Activity)
   
    // validate inputs
   
    // validate outputs
    
    // validate return
    
    dispatch.Invoker = new OperationInterceptorInvoker() 
    { 
      OperationDescription = description, 
      InnerOperationInvoker = dispatch.Invoker, 
      Activity = activity 
    };
}

You can see, there are some processes of the creating activity from the xaml resource. First of all, we have to obtain this resource from the Repository (in this implementation, it is a File System, but it can be any storage), then creating DynamicActivity from the xaml resource, validation arguments and return value.

The following code snippet shows a fragment of the creating DynamicActivity from the byte[], where xaml resource was loaded:

#region create WorkflowElement
WorkflowElement activity = null;
using (MemoryStream ms = new MemoryStream(xamlbytes))
{
  activity = WorkflowXamlServices.Load(ms);
}

if (activity == null || activity.GetArguments() == null)
    throw new InvalidOperationException("The Activity is not root ...");
#endregion

And the following code snippet shows how the return value for Request/Response message exchange pattern is validated

#region validate return
if (description.IsOneWay == false)
{
  MethodInfo mi = null;
  
  if (description.SyncMethod != null && 
    description.SyncMethod.ReturnType != typeof(void))
  {
    mi = description.SyncMethod;
  }
  else if (description.EndMethod != null && 
    description.EndMethod.ReturnType != typeof(void))
  {
    mi = description.EndMethod;
  }
    
  if (mi != null && activity.GetArguments().FirstOrDefault(e=>e.Direction 
    == ArgumentDirection.Out && e.Type==mi.ReturnType && e.Name=="_retval") == null)
  {
    throw new ArgumentException("Missing output argument in the xaml", "_retval");
  }
}
#endregion

Once we passed the first configuration step (before the host is opened) without any exception, we can assume that xaml method can be invoked properly with matching types for inputs, output and return value. Note, that our implementation is designed for caching the DynamicActivity, therefore we cannot change xaml method on the fly.

The WCF Framework will call our custom operation invoker based on the invoking pattern. The following code snippet shows a sync pattern of the invoking method:

object IOperationInvoker.Invoke(object instance, object[] inputs, 
	out object[] outputs)
{
  outputs = new object[0];

  // method info
  MethodInfo mi = OperationDescription.SyncMethod;
  
  // dictionary of inputs 
  var inArguments = this.InArguments(mi, inputs);

  // invoke xaml method
  var outArguments = WorkflowInvoker.Invoke(Activity, inArguments, Timeout);
  
  // dictionary of outputs
  object retVal = this.OutArguments(mi, out outputs, outArguments);

  // done
  return retVal;
}

As you can see, the above method is very straightforward. Thanks for WF4 "horse class" to process all magic for xaml method. We need only package arguments into Dictionary object like it is shown in the following code snippet for outputs and return value:

private object OutArguments(MethodInfo mi, out object[] outputs, 
   IDictionary<string, object> outArguments)
{
  outputs = new object[0];

  if (OperationDescription.IsOneWay || 
      mi.ReturnType == typeof(void) || 
      outArguments.ContainsKey("_retval") == false)
  {
    return null;
  }

  string[] outParamName = (from pi in mi.GetParameters() 
                           where pi.IsOut 
                           select pi.Name).ToArray();
  
  outputs = new object[outParamName.Count()];

  for (int ii = 0; ii < outParamName.Count(); ii++)
  {
    outputs[ii] = 
     outArguments.FirstOrDefault(e=>e.Key==outParamName[ii]).Value;
  }
  return outArguments["_retval"];
}

Parameter XamlPath

The OperationWorfklowInvokerAttribute has implemented one string type parameter such as XamlPath to explicitly specify a path of the xaml file location. This value is depended from the hosting environment. In the case of the IIS hosting, the site is the base location path. The following code snippet shows a few examples for this parameter:

namespace WcfService1
{
  public class Service1 : IService1
  {
    //[OperationWorkflowInvoker( XamlPath="App_Data/")]
    //[OperationWorkflowInvoker( XamlPath="App_Data/GetData.xaml")]
    [OperationWorkflowInvoker]
    public string GetData(int value) 
    { 
      return null; 
    }
  }
}

Note, that the default name of the file is based on the namespace, contract name, method name and extension xaml, therefore for the above example its name is WcfService1.IService1.GetData.xaml.

The second case of the attribute shows its change for location and name within the site.

That's all for implementation and let's look at the usage and testing this magic attribute, which switches a service operation to the "workflow world"

 

Usage

The usage of the custom operation for WorkflowInvoker is very straightforward and requires the following::

Step 1. Installation of the VS 2010 and .NET Framework 4 (currently in the Beta 1 version)

Step 2. Downloading OperationWorkflowInvoker.dll assembly from this article and adding to your project.

Step 3. Attributing a service operation by OperationWorkflowInvokerAttribute like it is shown in the following test example:

Image 6

 

Step 4: Using VS 2010 for creating xaml methods, the following are screen snippets of our test example:

<h3h4>Xaml Method "SayHello"

 - This method mediates a loopback feature, when the input information is delivered back to the caller. The root is a Sequence activity.

Image 7

 

Xaml Method "DivisionOfIntegers"

 - This method mediates a feature of the division of integer numbers. For the demonstration purposes, the root activity is represented by Try/Catches/Finally activity. You can see, there is Sequence activity named as Calculator inside of the Try.

Image 8

 

For testing purposes, this article includes a small tester. Launch the console program WorkflowConsoleTester.exe and let's play with xaml methods such as SayHello and DivisionOfInteger. The tester has a hardcoded address: http://localhost/Service.svc

For the client side, we can use a generic Microsoft WCF Test Client as it is shown in the following screen snippet:

 Image 9

 

The next test can be done for service operation hosted on the IIS/WAS. The article includes a project WcfService1 from the template Web gallery. This template project has a pre-coded a service with simple contract. I modified this code by injecting an OperationWorkflowInvoker like it is shown in the following code snippet:

Image 10

This last test can demonstrate a capability of the custom operation invoker for invoking a business logic declared in the xaml stack. Only the selected operation is handled by workflow without impacting the other ones, in our example the clr method GetDataUsingDataContract.

 

That's all for this article, but there is an one more thing, let's describe it in the following paragraph.

 

Appendix

WF4 WorkflowInvoker is really "magic" class for invoking a workflow activity in the synchronously manner. It's a perfect component for plumbing design to encapsulate a business logic for its declaratively changes.

In my article, Workflow Adapter/Connector Pair, I published (three years ago) the implementation of the WorkflowInvoker based on the WF 3.0 Technology. I described a plumbing design and implementation for Remoting. The plumbing concept was based on creating a custom client and server channel sinks. Having this custom sink implemented by WF4 WorkflowInvoker will enable to replace a remote object by workflow without impacting the code, just a small change in the remoting configuration section located in the config file.  

Originally, I have planned to include the implementation of the remoting custom server sink as an adapter for invoking WF4 Workflow, but later I changed my mind to use this time to write next article about the WF4 Designer hosted on the MMC (see the bellow screen snippet), so I hope it will be on the codeproject, soon.

 

Image 11

 

Conclusion

In conclusion, this article described a usage of the WF4 WorkflowInvoker for plumbing purposes with WCF Framework. This custom operation invoker allows your application to make a first "baby step" into the workflow world. This solution give you great flexibility for your application using the Microsoft xaml stack for runtime projecting instead of building your own infrastructure. I hope you enjoined it.

Note, this article has been written using the .NET 4 Beta 1 and I will update it when Beta 2 is publicly available.  

 

References:

A Developer’s Introduction to Windows Communication Foundation (WCF) .NET 4 Beta 1

A Developer's Introduction to Windows Workflow Foundation (WF4) in .NET 4 Beta 1

 

 

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

 
Generalpersistance Pin
marfin13-Dec-10 22:29
marfin13-Dec-10 22:29 
GeneralMy vote of 5 Pin
marfin13-Dec-10 22:27
marfin13-Dec-10 22:27 
Generalcan't be opened by beta2 Pin
then52025-Nov-09 15:33
then52025-Nov-09 15:33 
GeneralRe: can't be opened by beta2 Pin
Roman Kiss25-Nov-09 20:34
Roman Kiss25-Nov-09 20:34 
QuestionRe: can't be opened by beta2 Pin
MyPaq2-Dec-09 0:12
MyPaq2-Dec-09 0:12 
GeneralThank you Pin
Member 456543321-Sep-09 1:56
Member 456543321-Sep-09 1:56 
GeneralYou're already ahead of the pack Pin
Daniel Vaughan3-Sep-09 23:28
Daniel Vaughan3-Sep-09 23:28 
GeneralRe: You're already ahead of the pack Pin
Roman Kiss4-Sep-09 7:40
Roman Kiss4-Sep-09 7:40 
GeneralRe: You're already ahead of the pack Pin
Daniel Vaughan4-Sep-09 8:18
Daniel Vaughan4-Sep-09 8:18 

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.