Sometimes I wonder... Other times I know.
Thursday, September 29, 2005
 
Local Communication Services Explained
As promised. Here is a small tutorial/walkthrough/explanation of local communication services. There are probably other write-ups available, I just felt compelled to write this one up. This applies to Windows Workflow Foundation Beta1 and is still subject to change.



Introduction
Among many, one of my favorite explanations of a workflow… “a workflow is responsible for coordinating work which exists outside its own context. “

Given this definition, being able to communicate to entities external from the workflow is key to a successful workflow platform. There are many means of communications within Windows Workflow Foundation (queues, web services, parameters) but the one I’m writing about today is called Local Service Communication.

Local Service Communication allows a workflow to communicate with services/objects which reside within the host.

I’ve chosen to write about this one because from experience, it seems to be the most difficult topic to understand within the entire Windows Workflow Framework. To that point, I’m not going to talk about advanced messaging constructs like correlation, in-order delivery, convoys etc.

For the purposes of this blog entry, let’s take a simple loan approval scenario.

A host application will submit a loan application which includes loan name and loan amount to the workflow. Depending on the loan amount, the workflow will return back to the host the approval or rejection of the loan.

In this sample, our host application will just be a simple console application.

Moving Parts
As depicted above, there are three ‘core’ moving parts you need to think about when the local communication services


  1. The Communications Interface

  2. The Workflow

  3. The Host/Application

Conceptually, the single most important concept that will help you understand this topic is:

Data from the host to the workflow is passed through events.
Data from the workflow to the host is passed through method parameters.


Communications Interface
You can think of the communications interface as a contract between the workflow and the host application. This contract defines how the workflow and host will communicate with each other.

This contract is relatively straight forward. It’s a .NET interface with methods and events. You can see an example below. We’re exposing two methods and event. The methods are used by the workflow to notify the host if the loan is rejected or approved. The event is used by the host to submit the loan application to the workflow. Notice the [DataExchangeService] attribute.



You’ll notice that LoanEventArgs derive from WorkflowMessageEventArgs. Further, the constructor takes the LoanName and LoanAmount as arguments, but it also takes an instanceId which is passed to the base. The instanceId will be provided to us by the host (shown and described later) and is used to identify which workflow instance the host is sending and receiving messages from. You events must always derive from WorkflowMessageEventArgs.

As you’d expect, we’re also defining the loan name and loan amount as part of the event args.



The workflow
The communication interface (contract) between the host and the workflow has been established. Now the workflow needs use that contract to send and receive to the local service.

Just to re-cap, this workflow determines if the loan approval is accepted or rejected. The workflow will get the loan information from the host (via the event), then make a decision based on a rule (not covered in this blog entry) to determine if it should be approved or rejected. A simple diagram is shown below.

The way the workflow communications through the interface and to the host is through two activities: EventSink and InvokeMethod.

Conceptually, think of getting data into the workflow through EventSink(s) and pushing data out of the workflow through InvokeMethod(s).



The code below shows the definition of the EventSink activity within the workflow. You can see that the activity’s interface type is set to ILoanService and it’s syncing with “SubmitLoan”. The activity also pulls out the Loan ID (the name) and Loan Amount through parameter bindings. Note: this code would be generated for you if you use the designer.



The code below shows the definition of the InvokeMethod activity within the workflow. You can see that the activity’s interface type is set to ILoanService and it’s calling the “LoanApproved” method. Again, this code would be generated for you if you use the designer.



Host
Ok, we’ve defined the communications interface and the workflow we want to use to back our app.

The first thing we need to do is provide an implementation of the communications interface. This class will inherit from the ILoanService and provide the implementation for the two methods we defined. These are the methods which will be called by the workflow via the InvokeMethod activities. In this case, the implementation just outputs to the console.

As part of the implementation of the interface, we also need to implement the SubmitLoan event. You can see that I’ve created a method which then calls the event with the instanceID, the name of the loan and the loan amount. Exactly as is defined in the EventArgs.



We still need to get the instance ID from the runtime. But before we do that, we need to register our local communications service with the workflow runtime. As you can see below, we create a new LoanService and register it with the runtime. You’ll notice that the LoanService class also has an instanceId field. This value is the unique identifier of the workflow instance. Finally, we call the SubmitLoanToWorkflow we defined in our implementation of the communications interface, which will thus raise the event and kick off the workflow.





Comments:
Hi Dennis, this post is just great; we have been doing some related work and maybe you can help us with something.
Our goal is to use a workflow as a navigation map for a website.
In a given page of this website, the workflow should tell what other pages the user can go, and after the user clicks on some link, the workflow receives some data, makes some queries, etc, and then gives the website the URL of the next webpage.
The workflow type we have chosen is a state machine because of the navigation map usage of it.

We’ve tried many approaches in order to make this work, but none of them succeeded:
- Web Services: we exposed the workflow as a web service and then call it from the website. The problem is that the workflow runtime is created when the web service starts and then destroyed after it responds.
This way we can’t store relevant data on the workflow and every time the user clicks on a link, the workflow should start all over just to move from one step to the other.
- Local Communication: (we’ve used the labs example but your example should do as well) we raise an event from the website which is listened by the workflow, do its stuff and then invokes a method to responds the website.
The problem is that as the website receives the workflow response, it has already lost its chance to make a Response.Redirect for example, since it’s an asynchronous schema and after the website raises the event, the control gets back to the client.

So, the obvious question is: how can we make it work?

Thanks in advance... Ariel Schapiro, Jonathan Menasches.
 
Hi Ariel,

In your posting you say "This way we can’t store relevant data on the workflow and every time the user clicks on a link, the workflow should start all over just to move from one step to the other".

It seems to methat Workflow Persistence was designed exactly to address this issue:

(from the PDC2005 "COM327 Hosting and Communications Options in Workflow scenarios" slides)
private void RunWorkflow()
{
WorkflowRuntime wr = new WorkflowRuntime();
string connectionstring = "Initial Catalog=Persistence;Data
Source=localhost;Integrated Security=SSPI;";

wr.AddService(new SqlStatePersistenceService(connectionstring));
wr.AddService(new SqlTimerService(connectionstring));
wr.StartWorkflow(typeof(SimpleWorkflow));
}

By the way, I like this idea of yours to control the navigation using a Workflow.

Kinds Regards,
Jean-Paul
 
Jean-Paul,

What about if a have a web page that calls a workflow exposed as a web service and I want to persist that workflow state? That way I can make different calls to that web service and have control of its state...

Is it possible???

Thanks!!
Ariel Schapiro.
 
Hi Ariel,

Regarding the problem with your second approach: You can replace the default threading service (which is async) with the AspNetThreadingService which is sync.

Just add this to the WorkflowRuntime/Services section of your web.config file:

<add type=
"System.Workflow.Runtime.Hosting.ASPNetThreadingService,
System.Workflow.Runtime, Version=3.0.00000.0,
Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>

Max
 
Post a Comment

<< Home