Contents
Note: This article has been written using the .NET Framework 3.5 (Orcas) Beta 2 version.
Building an event driven application solution based on the Logical Connectivity Model (LCM) to encapsulate the end-to-end business layers from the physical connectivity requires using appropriate technologies across all tiers. The typical multi-tier architectures with a private middle-tier are smoothly changing to the distributed middle-tier based on the Service Oriented Architecture (SOA).
The latest technology such as AJAX (Asynchronous JavaScript and XML) enables moving the physical connectivity of the LCM to the browser client side. This process is enabled by XmlHttpRequest ActiveX object hosted in the browser for dispatching and processing requests and responses from the server in the asynchronous manner without requiring a postback.
The XmlHttpRequest
uses the REST architecture style to exchange data in text format. Based on the Content-type the text (data) can be encoded/decoded between the browser and the service. This very efficient and language independent data-interchange is described by JSON (JavaScript Object Notation) format which can simplify its implementation task dramatically instead of using an XML document oriented style.
Therefore, the buzzword AJAX/JSON is frequently used in the modern web event driven architecture. The AJAX/JSON Enabled Web Page enables to decouple a page into small parts and handle them individually in an asynchronous manner. In other words, the group of user controllers can be handled asynchronously without requiring a postback to the web server for all page update processes.
From the server side, the service must be enabled for AJAX/JSON communications. This is coming in the next version of the .NET Framework 3.5 called as Orcas, currently in the beta version. This article is based on this beta version using new webHttpBinding
and JSON encoding. You will see more details later.
I assume you will read more details about AJAX, JSON, XmlHttpRequest, REST to understand their style, architecture and position in the Front-end layer and especially on the browser client side. Now, it's the time to explain the roles of the WCF and WF Technologies in the browser/workflow connected systems.
The following picture will help me to show a position of these technologies in the event driven Enterprise solution:
The above solution is driven by metadata containing a description of the End-To-End Activity Definitions such as business logic behind the WCF Service, presentation of the Web Page with definition of the components, their interfaces and data contracts. Each Web Page has its own entry definition in the knowledge base to factory the page and enable its components for event driven tasks. Of course, on top of that, there can be a root definition to navigate all sites - products etc. During the page activation, the workflow state machine instance identifier is sent to the browser to send an event message based on the event interest.
How it works?
- When the user creates an event (for instance, clicking on a button) on the specific component on the page, its logic implemented by JavaScript will create a request object (event message) represented by JSON formatted text, this process activates an AJAX Callback for the service response and calls the magic
XmlHttpRequest
object to dispatch a request to the specific endpoint. - On the server side, we have a WCF service endpoint enabled for AJAX/JSON communications. The incoming message, after its JSON deserializing is forwarded to the workflow instance queue based on the Interface contract. Note, all layers are also configured by the knowledge base.
- The response from the service is received in the AJAX callback handler in a non-blocking process.
Hosting and plumbing WCF and WF models is very smoothly implemented in the .NET Framework 3.5 (beta version). There are two additional context driven workflow activities for Send and Receive messages based on the Interface contract.
From the logical connectivity point of the view, the event interest from the browser is delivered across the AJAX/JSON/WCF/WF Technologies into the business logic - event sink represented by workflow activity. All of this communication is fully transparent and configurable between the publisher of the event interest and its consumer.
The reusability of the business activity behind the WCF service is available by adding additional endpoint(s) to the service. The concept of the firing of WF activities behind the WCF service is described in details in my article Fire WorfklowEvents from WCF and this assembly will be used in this article.
The primary goal of this article is to focus on firing an event from the AJAX/JSON enabled page to the workflow activity via the WCF service (such as another client). Note, there isn't much implementation to be done here as the magic reusable code is already available in my above mentioned article.
Well, let's start.
The following picture shows the logical connectivity between the event sink represented by the workflow activity and event source located on the browser client. In this case, for a long running workflow, the browser is going to raise an event in the fire&forget manner:
In order to receive an event message by event driven Activity, the ExternalDataExchange
Interface contract must be declared with an application specific event handler.
The event delegate has a standard .NET signature shown in the following line:
public void EventHandler(object sender, ExternalDataEventArgs eventArgs);
where eventArgs
represents a Workflow event interest such as InstanceId, Identity, application specific data, etc.
The event handler, in the JSON format, will have the following notation:
{"sender":"myName",
"e":{"batchworkHandler":null,
"batchworkItem":null,
"identity":null,
"instanceId":"00000000-0000-0000-0000-000000000000",
"waitForIdle":true
}
}
As you can see, the event message can be easily created on the browser side and be delivered to the service endpoint to deserialize into the CLR.
We need to perform 3 simple steps in order to accomplish the AJAX/JSON/WCF/WF connectivity:
Step 1: Server - Hosting and plumbing WCF + WF Technologies using .svc file (inline code)
JSON enabled service:
The @ServiceHost
directive enables invoking the host activation type declared by the Factory
attribute. Note, this custom class must be derived from the ServiceHostFactoryBase
class in order to call its abstract method for creating the ServiceHost
and WorkflowRuntime
objects. More details about this directive can be found here.
There is no attribute CodeBehind
, therefore all implementation is located (in-line) in this .svc file for runtime compiling. The service contract, in this example, class ServiceTest1
is a regular WCF service with one operation method, for incoming (one way) message request. Decorating this method with the custom attribute RKiss.WorkflowEventService.WorkflowFireEventAttribute,
the operation invoker will forward a message to the workflow queue based on the Interface contract and e.InstanceId
value. Note, the method parameter can be any serializable complex type derived from the ExternalDataEventArgs
. More details about invoking a workflow and passing a message to its queue is described in my previous article Fire WorkflowEvents from WCF; Here we are using its assembly.
That is all for this step. The next step is to create a configuration section for the AJAX/JSON enabled service endpoint.
Step 2: Service Configuration - Creating AJAX/JSON endpoint
We need to use the webHttpBinding
binding to enable an endpoint for AJAX/JSON conversations using the EnableWebScript
in the endpoint behavior and set the data format for JSON encoding in order to enable the usage of eval function in the JavaScript for deserializing the service response. Note, the fire&forget event message (no return value) doesn't need to set the messageEncoding
attribute.
Now, we have done all things on the service side, and we are ready to receive an AJAX/JSON request.
Let's do the last step - the browser client.
Step 3: Client - AJAX/JSON enabled JavaScript
The following code snippet shows a simple implementation of the AJAX/JSON enabled web page using JavaScript. There are 5 steps to process a firing event to the target endpoint:
var xmlHttp ;
try {xmlHttp=new XMLHttpRequest();}
catch (e) {try {xmlHttp=new ActiveXObject("Msxml2.XMLHTTP");}
catch (e) {try {xmlHttp=new ActiveXObject("Microsoft.XMLHTTP");}
catch (e) {alert("This sample only works in browsers with AJAX support");
return false;}}}
xmlHttp.onreadystatechange=function()
{
if(xmlHttp.readyState==4 )
{
}
}
var url = "service11.svc/ajaxEndpoint/Approve";
var body =
'{"sender":"AjaxClient",
"e":{"batchworkHandler":null,
"batchworkItem":null,
"identity":null,
"instanceId":"2c6b451e-0d7d-4e8b-bc71-8f51e5bf54b0",
"waitForIdle":true
}
}';
xmlHttp.open("POST",url,true);
xmlHttp.setRequestHeader("Content-type","application/json");
xmlHttp.send(body);
First of all, we need to create an ActiveX
object to handle all magic requests and respond (callback) data exchange in the asynch manner. Next, the JavaScript will create arguments for communications such as URL, request body in the JSON format and set a Content-type header for JSON conversion. After that, the send
method is called. Note, the method will immediately return back without blocking a thread for response call, that's why we set the callback function. In other words, the browser is ready to make another event on the page and so on.
As you can see, all steps are very lightweight. Based on this AJAX/JSON/WCF/WF plumbing, we can see more capabilities on the browser client. For example: The page can process the firing of group of events (something like broadcasting message(s) for a specific service contract) within the logical conversation and await the collection of all responses in one event sink (callback function) and then distribute them back within the page. This scenario is useful in the case of long running workflows.
Pre/Post Processing for Firing Workflow
Forwarding an event message from the event source (browser) to the workflow instance queue requires knowing the instance identifier (guid
) and Interface contract to create the workflow queue name. The ExternalDataEventArgs
has an instanceId
property for this purpose, therefore the event source should somehow obtain this "ticket id". Otherwise, the service has a responsibility to insert the ticket id before firing an event to the workflow. The WorkflowFireEventAttribute
has a capability of pre or post firing a process by executing logic in the operation contract.
The following code snippet shows how the pre-processed firing of a workflow based on the type can be accomplished. Note, the operation contract between the browser and service can have more application specific arguments to perform specific pre-processing.
Pre-processing firing the event:
As you can see, there is an option to prepare a process for firing a workflow
event in the above code snippet. In this example, the workflow
instance has been created based on the specific workflow
type.
We can also pass some initial parameters to the workflow
during its creating from the service or from the browser. The return
value from the service is going back to the browser, after forwarding the event to the workflow
. This operation can be considered as the beginning of the conversation with workflow
business logic.
Any other firing events can be in the fire&forget manner until the last operation contract will close this conversation and return a response from the workflow
.
The following picture shows an example of the event driven AJAX page to the State Machine Workflow:
and an example of the implementation of an event driven conversation between the AJAX and Workflow can be like it is shown in the following code snippet:
[ServiceContract]
public class ServiceTest12
{
[WorkflowFireEvent(Option = WorkflowFireEventOption.After,
EventName = "Approve",
EventType = typeof(IWorkflowSimpleEvents))]
[OperationContract]
public string BeginApprove(object sender, ExternalDataEventArgs e)
{
WorkflowRuntime wr =
OperationContext.Current.Host.Extensions.Find<WorkflowRuntime>();
WorkflowInstance wi = wr.CreateWorkflow(typeof(Workflow1));
e.InstanceId = wi.InstanceId;
return e.InstanceId.ToString();
}
[WorkflowFireEvent(EventType = typeof(IWorkflowSimpleEvents))]
[OperationContract(IsOneWay = true)]
public void Approve(object sender, ExternalDataEventArgs e)
{
}
[WorkflowFireEvent(Option = WorkflowFireEventOption.Before,
EventName = "Approve",
EventType = typeof(IWorkflowSimpleEvents))]
[OperationContract]
public string EndApprove(object sender, ExternalDataEventArgs e)
{
string retVal = "error";
AutoResetEvent waitHandle = new AutoResetEvent(false);
WorkflowRuntime wr =
OperationContext.Current.Host.Extensions.Find<WorkflowRuntime>();
wr.WorkflowTerminated += delegate {waitHandle.Set();};
wr.WorkflowCompleted += delegate(object sender1,
WorkflowCompletedEventArgs e1)
{
if(e1.WorkflowInstance.InstanceId.Equals(e.InstanceId))
{
int counter = (int)e1.OutputParameters["Counter"];
retVal = string.Format("{0}, counter={1}",
e.InstanceId.ToString(), counter);
waitHandle.Set();
}
};
waitHandle.WaitOne();
return retVal;
}
}
There are more possible scenarios for conversations between the browser and workflow, but let's look at plumbing of the WCF service in the separate assembly.
From the connectivity point of the view, the Workflow represents an event sink and the WCF client's event source (subscriber interest). The following picture shows this logical pattern:
There are two actual connections between the three layers such as AJAX, Service and Workflow. The Service is a regular WCF service with one or more endpoints. Each endpoint can be configured for a specific purpose based on the address, binding, contract and behavior.
The first connectivity (1), between AJAX and the Service is described in our example by IFireEventTest operation
contract (method). We can use this contract to receive an event message by service. Decorating an endpoint for AJAX connectivity using the webHttpBinding
will enable receiving a request from the AJAX enabled browser and dispatching it to the service operation (method).
The second interface contract is done by Workflow. Note, these contracts are fully independent and they don't expect to work with each other. They were designed separately within their own technology - see their decorations such as ServiceContract
and ExternalDataExchange
. The event driven workflow activity (HEEA) is creating a workflow queue based on the interface contract metadata and the workflow instance id. This is a key point for our plumbing concept to allow passing of a message body across the interface contracts.
Decorating a specific operation contract by the WorkflowFireEventAttribute
, the regular operation invoker will be replaced by WorkflowFireEventOperationInvoker
object for handling an incoming event message to the workflow in a transparent manner.
The first step for integration of WCF and WF is necessary to make their hosting. The following code snippet of the .svc file shows an example of the hosting ServiceTest2
:
The type described by the Factory
attribute will create the ServiceHost
and WorkflowRuntime
instances. The WorkflowRuntime
reference can be obtained via the host extension:
WorkflowRuntime wr =
OperationContext.Current.Host.Extensions.Find<WorkflowRuntime>();
As I mentioned earlier, there are two interface contracts and one service. The following code snippet shows an example of these items, including the custom ExternalDataEventArgs
.
[ServiceContract]
public interface IFireEventTest
{
[OperationContract(IsOneWay = true)]
[WorkflowFireEvent(EventType = typeof(IWorkflowSimpleEvents2))]
void Approve(object sender, MyEventArgs e);
}
[ExternalDataExchange]
public interface IWorkflowSimpleEvents2
{
event EventHandler<MyEventArgs> Approve;
}
public class ServiceTest2 : IFireEventTest
{
public void Approve(object sender, MyEventArgs e)
{
}
}
[Serializable]
public class MyEventArgs : ExternalDataEventArgs
{
}
As the above code snippet shows, the plumbing mostly requires a declaration of the interfaces and their attributes. The horse work is done in the WorkflowFireEventAttribute.
That's all about the AJAX/JSON/WCF plumbing to the standard .NET Framework 3.0 Workflow with a HandleExternalEventActivity
.
Let's test the plumbing concept.
Firing workflow from the AJAX enabled browser can be demonstrated by the following solution:
There are four web test pages and services for different scenarios in the above solution. Before the actual test, be sure that all services are installed in the virtual directory WFService. The following checks can be processed in the solution after successful compiling:
- Each Service should be opened by browser
- The Workflow1 should be opened by designer
To see what is going on the server side (including the workflow), please download the DebugView utility. This utility will show all Trace messages. Also, I notice, there is an assembly WorkflowEventService
from my previous article Fire WorfklowEvents from WCF in the ~/bin folder. And of course, the Orcas must be installed, including the SQL scripts.
OK, now we are going to fire events from AjaxClient12 page. The following picture shows an event source and workflow state machine:
For test purposes, the test solution has a simple workflow state machine with three states. Two states are event driven and the last one is a timeout state to complete this process when there is no activity for 10 seconds.
The above test page will generate an Approve(object sender, ExternalDataEventArgs e)
event to Worfklow1
. The page will allow to modify some parameters of the event message. When the BeginFireEvent
is pressed, the Approve
object is created in the JSON format (see the text box) and is sent via XmlHttpRequest
to the service12.svc. Based on the e.InstancedId
(what was initially empty), the service will insert a pre-processing call prior to the firing event. This fire pre-processing has a responsibility to create a workflow instance. After firing the event to the workflow, the workflow instance id is returned back to the client and shows up in the text box. The above picture shows this status.
The process of the firing event can be seen on the DebugView Form, like it is shown in the following picture:
Now we should press the FireEvent
button to generate another fire event to the workflow. Note, the fired event from the client has a lifespan of ten (10) seconds, otherwise the state machine will complete the workflow - see DebugView
Trace. Every click on the FireEvent
button will send one event message.
To complete this conversation with the workflow, press the EndFireEvent
button. In this case, the fire post-processing call will be invoked to wait for the workflow response in the blocking manner on the service side. After the timeout, the workflow will output a text message on the page with the number of event sent to the workflow.
We can start a new firing conversation to the workflow by clicking on the Clear button.
Based on this test, open other client pages for similar tests for example, page AjaxClient2.htm, where the event message has a custom object.
Our testing is completed.
This article described a capability of raising an event to the workflow using the Microsoft edge Technologies. In this solution, we can use a regular workflow built with .NET Framework 3.0. Upcoming new version knows as Orcas (.NET framework 3.5) will enable firing the event in the workflow context. The AJAX/JSON/WCF/WF represents a connectivity model driven by metadata where the business layers are properly encapsulated and deployed based on business needs.
References and useful links