Contents
- IOutputChannel
- IRequestChannel
- Send
- Begin/EndSend
- Ambient transaction flows in sync pattern
- OneWay and RequestReply message exchange patterns
- Untyped Message for input/output activity
- No require hosting a xaml activity
- Uri resource (xaml) addressing (including via Routing Service)
- Pull-up xaml activity from Repository (Database or File System)
- Multiple configuration of the Repository or Folder
- Persistence not supported
- ETW Tracking
- Usage by any client hosted by IIS/WAS, WinForm, Silverligth, Console, etc.
- .Net 4 Technologies
This article is a continuation to my article Routing Manager for WCF4 and previous articles such as Manageable Services and Contract Model for Manageable Services. In these articles, I have described in detail about the model driven architecture, where the Logical model is centralized (stored) in the Repository and then decentralized (deployed) to the target for its runtime projecting.
In this article, I will show you another way of business model composition during the runtime such as pulling the workflow activities from the Repository. This is an opposite of the Workflow Service, where a service is deployed and then called for its consuming using WCF connectivity model. In the pulling scenario, we have a Repository of the business xaml activities which can be pulled up for their execution in the caller domain.
Any virtual channel can be built thanks to WCF paradigm extensibility at the channel level. For our concept of the pulling and executing a xaml activity from the Repository we need to virtualize a client channel. Using the same channel pattern (transparent proxy pattern) enables us to invoke a workflow activity from business layer transparently in the loosely coupled manner. This custom channel is called a WorkflowChannel
. More details about the custom in-process WCF channel can be found it in my Null Transport for WCF article, which has been used for implementation of this WorkflowChannel
.
The idea for creating a WorkflowChannel
has been raised by the WCF4 Routing Service. The Routing Service is a great architectural component for routing messages, bridging protocols, message exchange patterns, versioning, balancing, error handling, pre and post message mediations, etc. Usage of the Routing service is very generic. From the architecture point of the view, it can represent a component for runtime business composition, something like a local service bus driven by metadata stored in the routing table. This service (different from others) has a built-in capability to be modified on the runtime without recycling a host process. In other words, the routing table can be changed remotely on the fly.
More precisely, we can dynamically change only outbound channels and routing rules in the Routing Service. Changes in the inbound channels will require to recycle a host process. It does make sense, for any listener/receiver in the service, isn't?
The Routing Service has a great capability to mediate forwarded message via interceptor. The interceptor can be plugged into routing process via a routing table. Note, the interceptor service must be deployed in prior of its usage, like another service. Any changes in the interceptor will need to recycle its process or deploy a new version and update a routing table.
This article shows you a different approach, where interceptors (mediators) can be deployed into the Repository and Routing Service can pull them up like any resource, based on the url address in transparently manner.
The following picture shows conceptual picture consuming xaml activities (such as message mediators) by Routing Service:
The above picture shows forwarding an inbound message into the xaml activity based on the message exchange pattern. In the case of RequestReply
message exchange pattern, the message is returned back to the router like from a "real" service in the full transparent manner.
The following picture shows in more details compared to the previously one, where you can see a custom channel for outbound message:
The above picture shows a position of the WorfklowChannel
in this scenario. The WorkflowChannel
is built only for specific protocol for client factory to obtain xaml resource from the Repository (database) and executing it like workflow activity in synchronously or asynchronously manner. In other words, the WorkflowChannel
doesn't have a listener, therefore it can only be used for outbound message such as client.
From the architectural point of the view, the Routing Service with a custom WorkflowChannel
represents a hosting service for unknown declaratively activities stored in the Repository. Activities can be invoked by external messages and then chained by individual activities. This composition is declared by routing metadata which also can be stored in the Repository for its bootstrapping by host process.
As I mentioned earlier, the activity resource is pulled up from the Repository based on the url query. The following picture shows a forwarding inbound router message to the custom outbound WorkflowChannel
:
As the above picture shows, the name of the activity (in this example: Test.ZipCodeByPass
) is part of the url query expression and it is forwarded to the custom WorkflowChannel
(net.wf
). Note, that this url query expression has been simplified in this article. We can create more sophisticated query expression, including topic, version, etc. This implementation supports pulling a resource from Repository, such as Database and/or FileSystem.
Invoking workflow activity via custom WorkflowChannel
can be done by any business layer. The following picture shows invoking a xaml activity via custom WorkflowChannel
using a Send
activity wrapped by custom BindingScope
:
As you can see, the Send activity is configured for custom binding wfTransport
, which is our custom WorkflowChannel
. The request message is passed into the _inMessage
argument of the xaml activity. When xaml activity has been completed, the output argument _outMessage
is passed as a reply message to the Receiver activity. Note, that _inMessage
and _outMessage
are required arguments for this RequestReply message exchange pattern. These arguments are untyped message, therefore the xaml activity has responsibility for deserializing and serializing (or creating) their payload.
Speaking about the message contract for WorkflowChannel
, there are two kinds of untyped contracts. One is for OneWay
and the other one is for RequestReply
. Both contracts are untyped messages similarly to the contracts using by Routing Service. Note, that the IOutputChannel
and IRequestChannel are
processing untyped message, but the transparent proxy can be a strong typed.
Basically, the Send activity can use any content such as Message, DataContract, MessageContract or parameters to create a message body, but on the other side, the ReceiverReply should use an untyped message.
One more example of the consuming a WorkflowChannel
. The following picture shows invoking a xaml activity from windows form via WorkflowChannel
:
Finally, based on the above description, the following picture shows a strategy of the Runtime Repository (container of the activities):.
The above picture shows two different scopes. The first one, which is a Design Time (including a Deploy Time) has responsibility for decomposition of the business model into small business oriented activities and deploying them into Runtime Repository. The other one is a Runtime scope that pulls activities from the Repository and consuming them by WorkflowServices, Services, Activities and etc.
Back to our Routing Service. In the following pictures we can see in details how the message can be forwarded (intercept) via custom WorkflowChannel
for its mediation:
The following picture shows a simple OneWay
pattern, where a received message by inbound endpoint can be forwarded directly to the outbound endpoint or to the outbound custom WorkflowChannel
for its mediation. In this case, the custom channel implemented a protocol IOutputChannel
(both synch and asynch patterns) that pulls-up the xaml resource based on the url query expression from the Repository, deserializing resource into the Activity
object, and executing this Activity
object by an instance of the System.Activities.WorkflowApplication.
Part of the Activity mediation is sending a message back to the Router for its forwarding to the specific outbound endpoint. As an example of the message mediation is for instance, adding a header for routing to the destination service. This is an OneWay pattern, therefore there is no exception message back to the caller if something happens in the mediator. Note, only tracking and logging shows what happen here.
As you can see, the above picture show you a very simple pattern to re-routing an inbound message to the mediator for its preprocessing and then back to the router for forwarding message to the target outbound. This change can be inserted without any glitches to the current router. We don't need to redeploy package, etc. In addition, having the correct Repository tooling system, the message can be intercepted in full transparent manner and admin/developer/business person can mediate message practically on the fly based on the needs.
The following picture shows forwarding a RequestReply message to the outbound endpoint via a custom WorkflowChannel
with a IRequestChannel
implementation. The channel processing is similar to previously one, except the channel waits for completion of the activity and replies response back to the caller. This is a tightly coupled pattern with an Activity including a forwarding message to the outbound endpoint, receiving its response and returning back to the caller. This scenario has a capability of the pre and post message mediation. In other words, the Activity can intercept request and reply messages. In addition, like the following picture shows, the interceptor activity can invoke another activity directly from the Repository (in transactional manner) or indirectly via routing service.
That's all for the introduction and concept. I hope you get a good picture and reason for the WorkflowChannel
needs.
Let's get started with Design and Implementation of the WorkflowChannel
. I am assuming you have some knowledge and experience with WCF and WF Technologies.
The concept and design of the WorkflowChannel
is based on WCF model extensibility at client channel level. Note, that the WorkflowChannel
features are built for client channel only, where the factory channel will pull-up a workflow xaml activity for its execution and based on the message exchange pattern will reply result back to the caller or process OneWay output. I will skip explanation how to build a custom WCF channel. You can find more details about this in my NullTransport for WCF article. I am going to focus on the specific features related to WorkflowChannel
, only.
The following picture shows a model of the WorkflowChannel
from the config, binding and channels point of the view:
As you can see, there is only one generic channel factory for both channels such as IOutputChannel
and IRequestChannel
.
The following screen snippet shows their model classes:
Both models are very simple, especially WorkflowOutputChannel
, which has only one way output pattern. Both models support message processing in the synch and asynch manner. There is a little bit different implementation between them. This difference is based on the hosting (executing) workflow activity. In the case of the synch process, the model is using a System.Activities.WorkflowInvoker
class for an activity executing. The reason is that this process can be done in the transactional manner.
The following code snippet shows the implementation of the Send method:
public void Send(Message message, TimeSpan timeout)
{
base.ThrowIfDisposedOrNotOpen();
base.RemoteAddress.ApplyTo(message);
WfHelper.WorkflowInvoker(message, this.connectionString, timeout);
}
For asynchronously message processing, the model channel will use a System.Activities.WorkflowApplication
class for executing a workflow activity. Thanks to this real horse power class, it makes our WorkflowChannel
very lightweight and elegant implementation:
public IAsyncResult BeginSend(Message message, TimeSpan timeout, AsyncCallback callback, object state)
{
base.ThrowIfDisposedOrNotOpen();
base.RemoteAddress.ApplyTo(message);
WorkflowApplication wfa = WfHelper.CreateWorkflowApplication(message, this.connectionString);
return wfa.BeginRun(timeout, callback, state);
}
public void EndSend(IAsyncResult result)
{
WfHelper.EndRun(result);
}
public Message EndRequest(IAsyncResult result)
{
return WfHelper.EndRun(result);
}
In the above code snippet, you can also see difference between the EndSend
and EndRequest m
ethods, where channel need to return reply message back to the caller.
OK, it looks like a very straightforward implementation, doesn't it? As you can see in the above methods, the implementation has been encapsulated into the WfHelper
static class.
Before we get into the implementation details about the WfHelper
utility class, let's look at where the xaml activity is stored and how we can addressed it.
Addressing resource (xaml Activity)
The concept of WorkflowChannel
is based on pulling a resource from the Repository. The resource at this point is a xaml activity which as an xml formatted text. As you know, based on previous description, the WorkflowChannel
is designed and built for factory and executing a resource in the caller domain. It is an in-process (local) channel. The channel is simplified without the encoder and transport level is virtualized for local in/out message passing. Therefore, RESTfull style is used for resource addressing, where a uri describes authority, path and resource query.
The following picture shows a syntax of the WorkflowChannel
addressing:
The authority part is showing a location of the Repository. This article has built-in two kinds of Repository, the first one is a repository stored in the database and the other one is repository stored in the File System called as folder.
The Path of the Uri addressing is optional. Without this value, the connection string for accessing resource is defined in the binding section of the wfTransport. In the case of folder, it is the location of the process assembly (exe file) or virtual directory (iis/was). This default path can be overwritten by configuration option, where different connection strings to the Repository or folder respectively subfolder can be used. You will see more examples, later on this article.
The Query of the Uri addressing is mandatory and it is used for query specific resource in the Repository. Its format starts with a key name of the resource such as xaml
. The query is very simple, but can be richer with expression of topic, version, etc.
The following picture shows some examples of the WorkflowChannel
addressing:
Note, that addressing a WorkflowChannel
via Router is slightly different as you can see in the above examples. In this case, the query part is behind the endpoint address. As you will see later, there is a "trick" implemented in the WfHelper
utility to obtain this query at the outbound channel of the Router.
Custom Binding
The WorkflowChannel
binding uses a custom binding for setup its "virtual transport". The following snippet shows an example of the WorfklowChannel
binding for standard tracking profile, specifying a database repository and message version Soap12:
<bindings>
<customBinding>
<binding name="wfAgentBinding">
<textMessageEncoding messageVersion="Soap12"/>
<wfTransport trackingProfileName="" repositoryConnectionString="...."/>
</binding>
</customBinding>
</bindings>
<tracking/>
The wfTransport
binding element has two properties such as trackingProfileName
and repositoryConnectionString.
The first one allows to specify a custom tracking profile. In the case when tracking section and profileName is empty, the standard TrackingProfile
is used. The second property such as repositoryConnectionString
allows to setup a specific database connection to the Repository, where xaml activities are stored.
Configuration Options
The default location of the Repository can be overwritten by configuration in the .config file. The following picture shows an example for both authoring such as folder and repository:
This is a great feature of the WorkflowChannel
that allows us to use alias name of the resource addressing and mapping to the physical one using a config file.
Xaml Resource (Activity)
The xaml resource is a workflow activity declared by xml formatted text (resource). It is stored like xml type in the repository. There is a contract defined by untyped message between the WorkflowChannel
and Activity.
The incoming message is stored in the _inMessage
argument and outgoing message is stored in the _outMessage
argument. These two arguments are mandatory for Activity invoked by WorkflowChannel
. The following screen snippet shows an example of the ByPass
Activity, where the input message is assigned to output one.
Note, that the Activity has a responsibility to create a proper message such as version, action, etc. for output argument. This message is a reply message back to the caller of the WorfklowChannel
. In the case of using a strong type channel factory (no untyped message), the output message format must math the strong type contract.
The following picture shows an example of using the custom activity CreateMessage
to generate a message based on the Contract-First model. There is an Editor for importing a requested contract (wsdl), selecting operation, version, etc. for generating a xslt parameterized template which will generate a proper soap message on the runtime. More details about this custom activity and others for message mediations can be found in my WF4 Custom activities for message mediation article.
One more note here, keep in mind, the WorkflowChannel
is basically an invoker of the activity for short time processing without a persistence and bookmarks. Its major advantage is using a common WCF paradigm to decouple a business model into small reusable business oriented activities (mediators) stored in the Repository.
Repository - Folder
The folder repository represents a simply container of the xaml resources. The following picture shows an example of the folder repository within the project. The folder App_Data is a container of the xaml activities, which can be designed by Visual Studio. Note, that the config file needs to map this path folder:
Basically, any folder can be physical mapped to be addressed by WorkflowChannel
. The folder can be explicitly addressed or relatively addressed to the location of the exe process.
Repository - Database
Using database for Repository of the xaml activities gives us more features on the enterprise level. We can control check-in/out resources, pre-validation, rollback, versioning, etc. To show the capability of the WorkflowChannel
, I will use Repository from my Enterprise Variables for WF4 article, where this design and tooling is described in details. Basically, the model of this Repository is shown the following picture:
The article includes a batch file to create and pre-load a small table of the Repository database. There is also batch file to clean-up database.
The following code snippet shows how simple it is to implement the get xaml resource from Repository:
private static string GetXamlFromRepository(string name, string connectionString)
{
string xaml = null;
using (var repository = new System.Data.Linq.DataContext(connectionString))
{
var query = repository.ExecuteQuery(typeof(string),
"SELECT runtimeValue FROM Variables WHERE Category=1 and Name={0}", name);
if (query == null)
{
string errText = string.Format("Workflow '{0}' doesn't exist in the Repository", name);
Trace.WriteLine(errText);
throw new Exception(errText);
}
xaml = query.Cast<string>().FirstOrDefault();
if (string.IsNullOrEmpty(xaml))
{
string errText = string.Format("Workflow '{0}' is empty", name);
Trace.WriteLine(errText);
throw new Exception(errText);
}
}
return xaml;
}
The above lines are placed if there is a change to the Repository model, for instance, using a stored procedure, etc.
WfHelper utility
The RKiss.WorkflowChannel.WfHelper
is a utility class for handling all functionality for sync and async processing a message from/to activity in the runtime. The following picture shows the class model:
The common part of the WorkflowChannel
is its addressing to the xaml resource. The following code snippet shows this implementation. The challenge for this part was to figure out this query for any direct or indirect consumer, for instance via a routing service. This implementation is done using a reflection to obtain original operation context and then value of the IncomingVia
property.
private static Dictionary<string, string> GetQuery(Message message)
{
string query = string.Empty;
using (var os = new OperationContextScope((OperationContext)null))
{
BindingFlags flags = BindingFlags.NonPublic | BindingFlags.Instance;
var originalScope = os.GetType().GetField("originalScope", flags).GetValue(os);
if (originalScope != null)
{
OperationContext originalContext = originalScope.GetType().GetField("originalContext", flags).GetValue(originalScope) as OperationContext;
if (originalContext != null && originalContext.IncomingMessageProperties.ContainsKey("IncomingVia"))
query = (originalContext.IncomingMessageProperties["IncomingVia"] as Uri).Query.Trim('?');
}
}
if (string.IsNullOrEmpty(query))
query = message.Headers.To.Query.Trim('?');
if (string.IsNullOrEmpty(query))
throw new Exception("The uri.query is empty");
var arr = query.Split(new[] { ';', '&' }, StringSplitOptions.RemoveEmptyEntries);
var dic = arr.Select(x => x.Split('=')).ToDictionary(i => i[0].ToLower(), i => i[1]);
if (dic.ContainsKey("xaml") == false)
throw new Exception("Missing 'xaml' value in the uri.query expression");
return dic;
}
As I mentioned earlier, for asynchronous message processing we use the System.Activities.WorkflowApplication
class to execute activity. Before creating this hosting instance, we need to do few things, such as pulling a xaml resource from the Repository, deserializing xaml resource into clr type and copy incoming message. The following code snippet shows a method to create an instance of the WorfklowApplication
for requested xaml resource:
public static WorkflowApplication CreateWorkflowApplication(Message message, string connectionString, string trackingProfileName)
{
WorkflowApplication wfa = null;
var query = GetQuery(message);
string name = query["xaml"];
string xaml = null;
string authority = message.Headers.To.Authority.Trim('/').ToLower();
string localpath = message.Headers.To.LocalPath.Trim('/').ToLower();
if(authority == "repository")
xaml = GetXamlFromRepository(name, localpath, connectionString);
else if(authority == "folder")
xaml = GetXamlFromFile(name, localpath);
else
throw new Exception( ... );
Activity activity = null;
using (MemoryStream ms = new MemoryStream(Encoding.UTF8.GetBytes(xaml)))
{
activity = System.Activities.XamlIntegration.ActivityXamlServices.Load(ms);
}
Message copyMessage = null;
using (MessageBuffer buffer = message.CreateBufferedCopy(Int32.MaxValue))
{
copyMessage = buffer.CreateMessage();
}
var inputs = new Dictionary<string, object>();
inputs.Add("_inMessage", copyMessage);
wfa = new WorkflowApplication(activity, inputs);
SetEtwTracking(wfa.Extensions, trackingProfileName);
#region events
#endregion
return wfa;
}
Note, that the asynchronous message processing is yielded at the start executing activity level. In the case of large complex mediations in the activity, the parsing and loading a workflow definition is actually done in sync manner. Also, the solution in this public article doesn't have an implementation of caching xaml resource and/or clr type workflow definition.
The end of workflow processing in the async pattern is defined by calling EndRun
method on the specific WorkflowApplication hosting instance. The following code snippet shows its wrapper implementation:
public static Message EndRun(IAsyncResult result)
{
ManualResetEvent wfc = new ManualResetEvent(false);
Message message = null;
BindingFlags bflags = BindingFlags.NonPublic | BindingFlags.Instance;
Type type = typeof(System.Activities.WorkflowApplication).GetNestedType("RunAsyncResult", bflags);
WorkflowApplication wfa = type.BaseType.GetProperty("Instance", bflags).GetValue(result, null) as WorkflowApplication;
object rpc = result.AsyncState.GetType().GetField("Rpc", bflags).GetValue(result.AsyncState);
FieldInfo fi = Type.GetType("System.ServiceModel.Dispatcher.ProxyRpc, System.ServiceModel, ...").GetField("Request", bflags);
Message request = fi.GetValue(rpc) as Message;
#region completed
wfa.Completed = delegate(WorkflowApplicationCompletedEventArgs e)
{
if (e.CompletionState == ActivityInstanceState.Faulted)
{
}
else if (e.CompletionState == ActivityInstanceState.Canceled)
{
}
else if (e.CompletionState == ActivityInstanceState.Closed)
{
if (e.Outputs != null && e.Outputs.Count > 0 &&
e.Outputs.ContainsKey(OutArgumentName) && e.Outputs[OutArgumentName] != null)
{
message = e.Outputs[OutArgumentName] as Message;
}
else
{
}
}
else
{
return;
}
wfc.Set();
};
#endregion
wfa.EndRun(result);
wfc.WaitOne();
return message;
}
As you can see, the above method is using a reflection to obtain a state from async result. It will be nice to have a public access to the workflow instance and request message. Anyway, I was lucky to get them using the reflection. Once we have a reference to the current workflow application instance, we can invoke its
EndRun
method to wait for workflow completion. To obtain a workflow completion state in this method, we need to use a manual synchronization object which allows us to create a response message in a proper way.
Ok, that was support for async processing. It should be noted, that the ambient transaction didn't flow through the WorkflowApplication
host in opposite to the WorkflowInvoker,
where it is supported. For this reason, the Send
and Request
methods of the WorkflowChannel
are implemented using a WorkflowInvoker. Its wrapper is implemented in the WfHelper
utility.
One more note, the solution in this article doesn't have a caching feature for xaml resource and its clr type.
That's all for this topic.
Now it is time to demonstrate the usage of the WorkflowChannel
.
In this paragraph, I am going to describe in details the usage and test of the WorkflowChannel
. The solution includes four projects like it is shown in the following picture:
The core of the solution is a WorkflowChannel
project and its implementation is based on the NullTransport for WCF channel. The others projects have been created for test purposes only. For your application usage, only the WorkflowChannel.dll
assembly is required. The above solution has multiple projects for consuming a WorkflowChannel
from different hosting domains and Repositories to demonstrate the capability of the WorkflowChannel
.
I divided the test cases into two parts based on the type of Repository. The simple part is for Repository Folder, where all xaml resources are located in the project folder (for instance: App_Data), therefore these projects are ready for testing. On the other side, when Repository is stored in a Database we need to make additional steps such as setup a database, using the re-hosted workflow designer, etc., as you will see later in the advanced Test B. section.
If you are interested in a simple test only, without creating a Repository in your SQL Server, please follow Test A section. Before our test, please review and provide the following prerequisites for usage and test of the WorkflowChannel
:
- Some knowledge and experience with WF4 and WCF4 + Routing Service
- Visual Studio 2010 (sp1)
- Download DebugView for Windows for trace output.
- Download this article's source solution
- Create a private transactional queue
Test
and assign full control to Everyone (only for Test.TxQueue
activity) - Launch
setup.bat
file (located in the Repository folder) to install a Repository on your SQL Server (only for Test B section) - Copy folder
WorkflowChannelTesters
located in the 3rdParty solution folder to your desktop. There are few .exe programs for our testers. Note, those assemblies can be found in my previously articles. We are going to use them for test purposes in this article. The following screen snippet shows four shortcuts testers:
- Launch the StartWebServer40.bat file to start the ASP.Net Development Server for hosting our Routing Service. The cmd program will ask you to type a directory, where this solution has been downloaded. After that, the following icon should be displayed on the taskbar:
- Check Routing Service access calling this address http://localhost:33975/workflow.svc
Test A - Examples using a File System Repository
Example A1 - ConsoleApplication1
This is a simple test for invoking activity by WorkflowChannel
directly or via a routing service. As an example of the activity, we will call a public service for weather <a href="http://ws.cdyne.com/WeatherWS/Weather.asmx">http://ws.cdyne.com/WeatherWS/Weather.asmx</a>.
The following screen snippet shows a diagram of the activity for this action:
Note, the input/output message of the above activity is Soap11 version. Of course, the activity can mediate message based on the needs, see the xaml activities in the router service, for instance. From the design point of the view, the above activity represents pre and post message mediator. It looks like a wrapper around the actually web service call. The activity is a good place to modify (massage) messages or create some lightweight composition with other services. For our simple test, the input/output activity contract is the same like a web service call, but it can also be completely different.
Ok, it is very important to have a proper configuration for this test such as an extension for the WorkflowChannel
binding, repository folder and custom binding for our two test clients:
So, the ClientApplication1 tester has built in two tests, the first one will use a client name "inprocess" (Soap11) and the second test will call a client with name "router" (Soap12). The above config file shows different custom binding for these clients.
Ok, let's launch the ConsoleApplication1 program. You should have the same result like it is shown in the following picture:
Example A2 - WcfClient1
This example has been built-in the WindowsForm with more user options such as change a request message, fire message or request/reply message to the specific endpoint. In this example, I will demonstrate invoking an activity Test.AddHeader
which will internally setup a message header and fire a message to another activity Test.Gaga
using a custom WorkflowChannel
binding. The following screen snippet shows this scenario:
As you can see in the above activities, there are very useful custom activities. The custom BindingScope
is a wrapper activity around the Send activity to customize binding and add a message header. More details can be found in my Dynamic Send Activity in WF4 article. The other interesting custom activity is a CreateMessage. This activity can be used for creating a message based on the Contract-First metadata. Please, see my other article for additional details WF4 Custom activities for message mediation.
Ok, launch the WcfClient1 tester, select address net.wf://folder/abc?xaml=Test.AddHeader
and press the button Fire. The result should be the same like it is shown in the following picture:
Once, the test passed, try to change the address for Fire or Invoke message. Keep in the mind, this test is only for Repository - Folder.
For example, selecting the address http://localhost:33975/workflow.svc/Folder?xaml=Test.ZipCodeByPass and pressing the button Invoke, the request message is sent to the Test.ZipCodeByPass
activity via a routing service. The activity is stored in the local folder repository (App_Data).
For tracing output messages from channel or activity, we can use DebugView for Windows program. For more troubleshooting details, the standard WCF and ETW tracking should be enabled.
Example A3 - WcfService1 - Routing Service
In this example, I am going to demonstrate "hosting" an activity in the process of the Routing Service. The following screen snippet shows this project.
Interesting thing about this project is that there is no a code. There is no single .cs
or .svc
files. The project is driven declaratively based on the config and xaml files. For our test, the above activity has been selected. This activity is actually a bypass activity, where the input message is passed to the output. There is no mediation.
Testing is very simple. The client (for instance WcfClient1) will send a request message to the router service, where the message is forwarded to the Test.ByPass activity via WorkflowChannel
. Inside of the activity, the message is assigned to the output argument and sent back to the channel. This message is returned back to the client. Note, that this is a RequestReply pattern scenario.
OK, let's do some testing. Please, select the http://localhost:33975/workflow.svc/Folder?xaml=Test.ByPass
and press button Invoke. You should get a request message back in the response text panel like is shown in the following screen snippet. You can change for instance a zipcode in the request message and invoke a message again.
Now, we can do some business in the activity. Let's call some web service within the activity which send a response back to the client. I already prepared this kind of test case. Please, select the address http://localhost:33975/workflow.svc/Folder?xaml=Test.ZipCodeByPass
and press button Invoke. You should see a response message from the weather service.
As I mentioned earlier, there is no code file. The routing service is configured by sections in the web.config file. Please, browse this file to look at content. Basically, you will see the following:
- service activation by relative address
~/workflow.svc
- for service endpoints based on the message exchange pattern and Repository storage (OneWay, RequestReply, Folder, Database)
- two clients, one for folder and the other one for database Repository
- custom bindings for inbound and outbound message version Soap12 (http and
wfTransport
) - routing table for simple forwarding messages from the specific inbound endpoint to the outbound one.
Test B - Advanced examples using a Database Repository
During this advanced test, I am going to show you a scenario which is more close to the production prototype. This test requires a Runtime Repository where are our test xaml activities are pre-loaded. In addition, you will get a small tool to create your activities and store them in the Repository.
First of all, let's play with a re-hosted workflow designer.
Example B1 - Workflow Designer
The solution package includes some assemblies that area available in my previously articles (see References) for test purposes. One of them is my Workflow Designer program, which is a re-hosted workflow designer in the WinForm. In this example, I will demonstrate calling an activity via WorkflowChannel
. Please, click on the DesignerTester shorcut to launch this program:
Using a WorkflowChannel
is straightforward. It will require setting a custom binding for Send activity and AddressUri
like it is show in the following picture. Note, that the message for Send activity is untyped message created in the custom activity CreateMessage.
Pressing the button Run, we can run this activity in the self-hosted console program. The result should be the same like it is shown in the following picture:
What happen there? There are two activities. The first one (master) will generate a request message for child activity invoked via WorfklowChannel
. This custom channel will pull-up the child activity from the Repository (Database) and process this activity within the host process (console program). The business is encapsulated into the child activity by calling a public web service (weather). The response from this child activity is returned back to the master activity and shown on the console. Very cool. We are using the same paradigm in transparent manner like calling a service. Besides that, we are pulling an activity from the container (Repository) without its hosting, it is not a workflow service. This is a pure xaml activity.
Example B2 - Send Message to MSMQ
In this test scenario, we have a client (WcfClient1), routing service (WcfService1), Runtime Repository, MSMQ and Admin Tool. The following picture shows their composition:
Select address http://localhost:33975/workflow.svc/Fire?xaml=Test.TxQueue
on the WcfClient1 and press the button Fire. The message is going to the Routing Service and based on the routing table is forwarded to the outbound endpoint with a custom wfTransport
binding. This custom WorkflowChannel
will pull up a xaml resource Test.TxQueue from the Runtime Repository, serializing into the clr type and executing by WorkflowApplication host object. This is a runtime process.
In the case, when we want to change a xaml resource on the fly, without any redeploying, glitches, etc., for instance, we want to tune (mediate) a message in the activity; we need some tooling support.
In this test, I am using a small tool from my previous Enterprise Variables for WF4 article, therefore I recommend to review it for more details on how to use this tool. Please click on the EnterpriseVariables shorcut to launch this tool:
Select a variable (activity) with name Test.TxQueuue and click on the Editor tab. Now, we have a capability of the re-hosted workflow designer to edit this activity:
Clicking on the Variables tab, we are back in the data grid and we can check-in these changes in the database.
That's all. I hope the above test cases shown you interesting features of the WorfklowChannel
.
ETW Tracking
The WorfklowChannel
added an extension to the WorkflowInstanceExtensionManager
for ETW tracking. The custom tracking can be configured in the config file and selected by its property in the binding section. The following picture is a screen snippet of the Event Viewer. Note, that the ETW log tracking must be enabled:
In conclusion, this article described a custom WCF channel for invoking a workflow activity in transparently way like a service. This custom channel enables us to encapsulate a business model into small reusable activities stored in the container (Repository). The activities can be pulled from the container during the runtime for their execution. Basically, the WorfklowChannel
can be used in the business layers to decouple layers in the same process. One of the advantages is for Routing Service, where this custom channel can be used for outbound endpoints, for message mediation and lightweight service composition. Besides that, the wcf paradigm, which is used for many MS Technologies, gives us reusable pattern for decoupling a business layers.
References
[1] NullTransport for WCF
[2] WorkflowInvoker for WCF4
[3] Routing Manager for WCF4
[4] Enterprise Variables for WF4
[5] Manageable Services
[6] Contract Model for Manageable Services
[7] WF4 Custom activities for message mediation
[8] Dynamic Send Activity in WF4