Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / C#

Host and Workflow: Two Worlds to Communicate - Part II

4.83/5 (15 votes)
3 Oct 2008CPOL8 min read 1   394  
Part II: Inter-communications: Workflow -> Host through the CallExternalMethod Activity.

Introduction

This is the second of five articles about Host Workflow communication.

In this series of articles, I will try to show the different possibilities to implement communication, from the simplest case to the more complex. I am not planning to be exhaustive, but I will try to give a general panoramic in order to get us a good idea about the thematic.

Because I also don’t like large articles, I have divided this series into the followings parts:

Background

In Part I, we understood how to pass information to a Workflow when it is launched. That works OK when you don’t need to return data from the Workflow. But often, you need to return information about the processed data in the Workflow.

The Workflow foundation is delivered with an Activity component to do this work: the CallExternalMethod Activity.

Returning to the example of Part I, our program reads a file from the computer and sees determinate number of chars from it. In this program, we want to pass two parameters (the file path and the number of characters to return from the file), and the Workflow will show the information in an internal message box. Now, we have the requirement to show the return information directly in the console output and not in an internal message box form (see Figure 1).

Image 1

You can see that the barrier between the threads is passed with the Workflow invocation and again to return the character from the file to the console host application.

To implement passing the information form the Workflow to the Host, WWF comes with a dedicated activity named CallExternalMethod. That is the good news; the bad news is that we must create a specific service pattern to communicate with this activity.

The communication service pattern works as another service. You must create a service interface, implement the service, and register it in the Workflow runtime instance. You can implement the communication as you want, by poll or by events, but you must fulfill the CallExternalMethod requirements. In short, you need to implement the following code structure:

Create the followings interface and classes:

  1. An interface with the method definition called by the CallExternalMethod Activity.
  2. A class that implements the interface. In this class, you implement the method that is called by CallExternalMethod. In this method, you made the code to send the information to the host.
  3. If you want to use Events to drive the communication, you should implement a specific EventArgs derived class to hold the information that you want to send to the host.

In the Host application, the following steps need to be taken:

  1. You must create a instance of the ExternalDataExchangeService class and register it in the Workflow runtime.
  2. You must register your created Communication Service in this data service class.
  3. If you use events to drive the communication, you must also handle the communication event in your host class.

In the Workflow:

  1. You must have a CallExternalMethod Activity.
  2. In it, register the Communication Interface and the method to call.

You can see the result class structure for our example in the following diagram:

Image 2

You can see that the CallExternalMethod makes a call to a method that is in another thread. When this method is executed, we have the results in the host thread! That is the trick.

We use a console application and a sequential Workflow to explain in detail how to create these structures.

Code Hands On

The best thing to do is download the code that is attached to this article, or if you have it from of Part I of this series, you can reuse it.

The companion project has the communication with the Workflow through parameters, that was explained in Part I. I assumed that you would know how to do it (if you don’t know, please revisit Part I of this article series).

We will now do a step by step explanation of the use of CallExternalMethod in our example:

A. Create the followings interface and classes

  1. An interface with the method definition called by the CallExternalMethod Activity.
  2. You can create this interface in the same project as your host or in an independent project. For simplicity, we created it in the same host application. The interface simply contains the method to be called by the Workflow. You should now be thinking, why not call any method in the host? The answer is, this interface must have a special header to define it as an external method callable by the Workflow. The complete code is shown here:

    C#
    ///<summary>
    /// You MUST to mark the interface with the header ExternalDataExchange
    /// to identify the interface as the communication interface. 
    /// </summary>
    [ExternalDataExchange] 
    public interface ICommunicationService 
    { 
       /// <summary>
       /// This method must be call by the CallExternalMethod Activity 
       /// to transfer the information to the console host application 
       /// <param name="response">Data to send to console</param >
        void SendDataToHost(string response); 
    }

    Observe the ExternalDataExchangeHeader. It determines that the method in the class can be called from the Workflow.

  3. A class that implements the interface.
  4. The code for this class is your responsibility and your decision. You can decide to implement a method to simply hold the data passed from the Workflow and then the host will poll the class instance, or you can decide if you want to implement an event to asynchronously get the data.

    In our code, we implement an event. We should pass the data from the Workflow in the arguments of the event:

    C#
    /// <summary >
    /// Implement the interface
    /// This implementation is made to comunicate WF -> Host
    /// </summary >
    public class CommunicationService: ICommunicationService
    {
    
        /// <summary >
        /// Event to communicate the host the result.
        /// </summary >
        public event EventHandler <SendDataToHostEventArgs> SendDataToHostEvent;
        /// <summary >
        /// Implement the external Method
        /// </summary >
        /// <param name="response" >response to host</param >
        public void SendDataToHost(string response)
        {
            SendDataToHostEventArgs e = new SendDataToHostEventArgs();
            e.Response = response;
            EventHandler<senddatatohosteventargs > sendData = this.SendDataToHostEvent; 
            if (sendData != null)
            {
                sendData(this, e);
            }
        }
    }

    You can see the implementation of the event and the simple code of the method above. The method only passes the response input parameter to the event arguments and raises the event.

    The definition of the event argument is trivial, and is shown here:

    C#
    public class SendDataToHostEventArgs: EventArgs
    {
       string _response = string.Empty;
       /// <summary >
       /// This property is used to pass in the Event
       /// the response to host
       /// </summary >
       public string Response
       {
           get
           { return _response; }
           set
          { _response = value; }
       }
    }

In our example, we create the three classes in different files. You can see them here:

  • SendDataToHostEventArgs.cs
  • CommunicationService.cs
  • ICommunicationService.cs

B. Register the Communication Service as ExternalDataExchangeService

We register it in the Workflow runtime instance in the host program. The intercommunication class Workflow-Host must be registered as an External Data Exchange Service in the runtime. The registration is relatively simple, as you can see in the following code:

C#
//Add support to the Communication Service..................

//Declare a ExternalDataExchangeService class
ExternalDataExchangeService dataservice = new ExternalDataExchangeService();
//Add to workflow runtime
workflowRuntime.AddService(dataservice);
//Declare our CommunicationService instance
CommunicationService cservice = new CommunicationService();
//Add to the ExternalDataService
dataservice.AddService(cservice);
//Add a handler to Service Event
cservice.SendDataToHostEvent += new 
  EventHandler<senddatatohosteventargs> (cservice_SendDataToHostEvent);
                
// end of support to Communication Service.................

The first four instructions are related to registering the intercommunication service, and the last to registering the event to communicate the result from the Workflow. Our application is now complete in the host side. Now we should make our Workflow.

C. In the Workflow…

Well, we use as base the same Workflow as in Part I. We suppress the code activity that shows the MessageBox. Then, drag and drop a CallExternalMethod activity to the Workflow, and configure it as in the following figure:

Image 3

Note that the InterfaceType property is filled with the created ICommunicationService. The MethodName is the method to be called by the activity in the interface. When you enter the MethodName, the property is expanded and you must declare the variable that will fill the response parameter. In our application exists a variable _response. To match the parameter with the variable, click in the ellipsis button in the Response field, and in the opened dialog, click in “Bind to a new member", create the field, and enter the name of the field _response. (If you click in the example code, the binding is already created, and you can see the variable directly in the “Bind to an existing member” screen).

The rest of the logic is trivial, and you can see it in the example (the logic to access the file and pass the initialization parameters from the Host).

Compile the application and launch it. You can use your proper parameters to launch the application or use the default. (Maybe you do not have the specific file that is used as the default. Feel free to change it for another that you have).

Resumes

The basic step to pass information from the Workflow to the Host using the CallExternalMethod Activity are the following:

Create the followings interface and classes:

  • An interface with the method definition called by the CallExternalMethod Activity and decorated with the header:
  • C#
    [ExternalDataExchange]
  • A class that implements the interface. In this class, you implement the method that is called by CallExternalMethod.

In the Host application:

  • Register a instance of the ExternalDataExchangeService class in the Workflow runtime.
  • Add the instance of the communication class to ExternalDataExchangeServiceInstance.
  • If you use an event to drive the communication, you must also handle the communication event in your host class.

In the Workflow:

  • Drop a CallExternalMethod Activity in the point of the Workflow that you want to send information to the Host.
  • In the CallExternalMethod Activity properties, register the Communication Interface, the method to call, and the variable or property to match the parameter of the called method.

Now, you know how to do a bi-directional communication between a host and a Workflow. But you will continue to have limitations. Until now, you only knew how to send information to a Workflow in the initialization phase, but what about sending information from the Host to the Workflow at any point of the Workflow? We will talk about this in the next part.

History

  • First version - 03.10.2008.

License

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