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

  1. Create a new Silverlight Application and add a Web project
  2. Add a new WCF Workflow Service to the web project (Do not set CanCreateInstance true on the receive activity for now)
  3. 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

  1. Right click and select Add Service Reference
  2. Discover the WCF Workflow Service you added to the web app
  3. Add a service reference
  4. 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.

  1. Open the Library Package Manager Console (all the cool kids use the console but there is also a UI)
  2. Set the Default Project to your web project
  3. 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