Using Workflow Services and Silverlight Together
In my previous post on WorkflowServices, CanCreateInstance and Silverlight I shared with you some of the pain that I went through in building my first Silverlight / Workflow Services app. Much of this pain was just because I have not done a great deal of work with Silverlight and WCF. In this post I’m going to give you some advice based on what I learned in building my Tech-Ed 2011 State Machine demo application.
Download Sample Code Windows Workflow Foundation (WF4) - Silverlight / State Machine Workflow Service
Tip: Get Microsoft.Activities and use SilverlightFaultBehavior
My colleagues on the Silverlight team told me about the SilverlightFaultBehavior that they included in the Silverlight-Enabled WCF Service template. The trick of this behavior is that it sets the response code of Faults to 200 (OK) so that Silverlight can pass the fault along to your code.
There is a great deal of WCF Judo that goes on to make this work but here is the essential piece that sets the response to HttpStatusCode.OK
public void BeforeSendReply(ref Message reply, object correlationState)
{
if ((reply != null) && reply.IsFault)
{
var property = new HttpResponseMessageProperty { StatusCode = HttpStatusCode.OK };
reply.Properties[HttpResponseMessageProperty.Name] = property;
}
}
Typically WCF Code applies this behavior with an attribute but with WorkflowServices you can’t do that. So I created the SilverlightFaultElement class that allows you to setup the behavior with configuration.
Tip: While Developing use Microsoft.Activities.Diagnostics.WorkflowServiceTraceBehavior
There I was trying to run my workflow service and wondering why it was not working. I had not written a unit test for it yet and I was wishing for the nice tracking information I got with the tracking extensions from Microsoft.Activities.UnitTesting. Then I had this idea… what if I built a WCF behavior that would setup tracking on the WorkflowServiceHost and then output the tracking info to the Visual Studio debug window.
So I created the WorkflowServiceTraceBehavior and WorkflowServiceTraceElement classes to enable the behavior via configuration.
How To Configure These Behaviors in your app
Step 1: Create a new Silverlight Application
- Create a new Silverlight Application and add a Web project
- Add a new WCF Workflow Service to the web project (Do not set CanCreateInstance true on the receive activity for now)
- Add a button to the Silverlight markup and add a click handler
<Grid x:Name="LayoutRoot" Background="White">
<Button FontSize="28" Click="Button_Click">Test</Button>
</Grid>
Step 2: Add WCF Configuration to your service
Add the following configuration to your web.config. At this point we won’t add the behaviors so you can see the problem.
<system.serviceModel>
<serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true" />
<behaviors>
<serviceBehaviors>
<behavior>
<serviceDebug includeExceptionDetailInFaults="true"/>
<serviceMetadata httpGetEnabled="true"/>
</behavior>
</serviceBehaviors>
</behaviors>
</system.serviceModel>
Step 3: Add a Service Reference in the Silverlight app
- Right click and select Add Service Reference
- Discover the WCF Workflow Service you added to the web app
- Add a service reference
- Add the code to call the service
private void Button_Click(object sender, RoutedEventArgs e)
{
var proxy = new ServiceClient();
proxy.GetDataCompleted += (o, args) => Debug.WriteLine("Result is " + args.Result);
proxy.GetDataAsync(123);
}
Step 4: Ready for the Pain?
Debug the application and click the test button. Here is what you will get
System.ServiceModel.CommunicationException was unhandled by user code
Message=The remote server returned an error: NotFound.
Huh? Not found? Don’t believe it my friends… that is not the real problem. Now let’s enable some behaviors and see what happens.
Step 5: Use NuGet to add Microsoft.Activities to your project
NuGet is so awesome – if you aren’t using it yet, get it now.
- Open the Library Package Manager Console (all the cool kids use the console but there is also a UI)
- Set the Default Project to your web project
- PM>Install-Package Microsoft.Activities
Now your web app will have all the goodness of Microsoft.Activities.
Step 6: Add the SilverlightFaultBehavior and WorkflowServiceTraceBehavior
Modify the web config to add the behaviors
<system.serviceModel>
<serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true" />
<extensions>
<behaviorExtensions>
<add name="silverlightFaultBehavior" type="Microsoft.Activities.ServiceModel.SilverlightFaultElement, Microsoft.Activities" />
<add name="workflowServiceTraceBehavior" type="Microsoft.Activities.Diagnostics.WorkflowServiceTraceElement, Microsoft.Activities" />
</behaviorExtensions>
</extensions>
<behaviors>
<serviceBehaviors>
<behavior>
<serviceDebug includeExceptionDetailInFaults="true"/>
<serviceMetadata httpGetEnabled="true"/>
<silverlightFaultBehavior />
<workflowServiceTraceBehavior />
</behavior>
</serviceBehaviors>
</behaviors>
</system.serviceModel>
Step 7: Test SilverlightFaultBehavior
Debug the application and click the test button again. This time you will get the real error.
Now you get the correct exception
System.ServiceModel.FaultException was unhandled by user code
Message=There is no context attached to the incoming message for the service and the current operation is not marked with "CanCreateInstance = true". In order to communicate with this service check whether the incoming binding supports the context protocol and has a valid context initialized.
Step 8: Test WorkflowServiceTraceBehavior
Now open your workflow service and set CanCreateInstance=true on the receive activity. Now your app should work correctly. Debug it again and in the output window you will see tracking information about everything that is happening in your service.
Activity <Sequential Service> state is Closed at 10:02:50.0799
{
Variables
handle:
data: 123
}
WorkflowInstance <Sequential Service> is <Completed> at 10:02:50.0939
WorkflowInstance <Sequential Service> is <Deleted> at 10:02:50.0969
Summary
I don’t know how I ever did WorkflowService development without the WorkflowServiceTraceBehavior. It makes it so easy to figure out exactly what is going on with my service. You can use it with any kind of WorkflowService so check it out.
Happy Coding!
Ron Jacobs
https://blogs.msdn.com/rjacobs
Twitter: @ronljacobs https://twitter.com/ronljacobs
Comments
Anonymous
May 27, 2011
Could you share the rest of the system.serviceModel section of web.config? I'm having trouble getting off the ground.Anonymous
May 27, 2011
That is the entire section of <system.serviceModel> in my sample code. What kind of trouble are you having?Anonymous
May 30, 2011
Nevermind, I got everything working. Thanks for this blog post.