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
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.
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.
<Grid x:Name="LayoutRoot" Background="White"> <Button FontSize="28" Click="Button_Click">Test</Button></Grid>
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>
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);}
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.
NuGet is so awesome – if you aren’t using it yet, get it now.
Now your web app will have all the goodness of Microsoft.Activities.
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>
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.
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.0939WorkflowInstance <Sequential Service> is <Deleted> at 10:02:50.0969
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 http://blogs.msdn.com/rjacobs Twitter: @ronljacobs http://twitter.com/ronljacobs
Could you share the rest of the system.serviceModel section of web.config? I'm having trouble getting off the ground.
That is the entire section of <system.serviceModel> in my sample code. What kind of trouble are you having?
Nevermind, I got everything working. Thanks for this blog post.