The WF4 Receive activity shields you from a lot of the WCF pipeline. Normally that is a good thing but there are occasions where you want to know more about the incoming WCF request. Normally you can use the WCF OperationContext.Current to get at this information but with a workflow service this doesn’t work as it is null. The reason is that the workflow executes on a different thread.
So how do you get at the OperationContext?
The trick is to add a class implementing the IReceiveMessageCallback interface to the NativeActivityContext.Properties while the receive is executing and this will have it’s OnReceiveMessage() function executed with the OperationContext passed in. This means creating an activity to wrap the Receive activity like this.
public class GetMessageProperties : NativeActivity
{
public Activity Body { get; set; }
protected override void Execute(NativeActivityContext context)
{
var inspector = new GetMessagePropertiesInspector();
context.Properties.Add(inspector.GetType().FullName, inspector);
context.ScheduleActivity(Body, OnBodyCompleted);
}
private void OnBodyCompleted(NativeActivityContext context, ActivityInstance instance)
{
var inspector = context.Properties.Find(typeof(GetMessagePropertiesInspector).FullName) as GetMessagePropertiesInspector;
if (inspector != null)
{
Console.WriteLine("ClientUri = {0}", inspector.ClientAddress);
}
}
}
The GetMessagePropertiesInspector, which implements the IReceiveMessageCallback, is quite simple. The main thing here is that it can be serialized along with the workflow state so make sure it contains the DataContract attribute.
[DataContract]
public class GetMessagePropertiesInspector : IReceiveMessageCallback
{
[DataMember]
public string ClientAddress { get; set; }
public void OnReceiveMessage(OperationContext operationContext, ExecutionProperties activityExecutionProperties)
{
var properties = operationContext.IncomingMessageProperties;
var endpoint = properties[RemoteEndpointMessageProperty.Name] as RemoteEndpointMessageProperty;
ClientAddress = endpoint.Address;
}
}
Creating and self hosting the workflow in a console app is easy, if you want to use the designer you will need to add an ActivityDesigner as well and you are good to go.
class Program
{
static void Main(string[] args)
{
var workflow = CreateWorkflow();
var host = new WorkflowServiceHost(workflow, new Uri("http://localhost:8080/MyService"));
host.AddDefaultEndpoints();
host.Open();
Console.ReadLine();
host.Close();
}
private static Activity CreateWorkflow()
{
var result = new Sequence();
var receive = new Receive();
receive.OperationName = "GetData";
receive.CanCreateInstance = true;
result.Activities.Add(new GetMessageProperties() { Body = receive });
result.Activities.Add(new SendReply() { Request = receive });
return result;
}
}
If you want to work with the reply message there is also a ISendMessageCallback which does exactly the same for the result message.
Enjoy!
www.TheProblemSolver.nl
Wiki.WindowsWorkflowFoundation.eu