When I started working with workflow (WF) in early November of 2005 I quickly realized I would need to use the Local Communication Services provided by WF. At the time most of the samples were using very simple data types which was good for getting started but the project I am working on required more complex objects to be passed into and out of the workflow. The use of these complex objects presented some very interesting issues that I had to figured out in order for things to work correctly. In this blog I am going to cover not only what the issues were but how I debugged them as well. The code sample I am going to use is a simplified version of my situation but still represents the issues that I faced. For an explanation of what Local Communication Services are I would recommend reading Dennis Pilarinos’s blog entitled Sometimes I wonder… Other times I know that covers Local Communication Services. Note that the blog is a little out date and does not use the new method of working with Local Communication Services (see Code Listing 1 and Code Listing 2) via the ExternalDataExchangeService (Code Listing 1) and the ExternalDataExchange attribute (Code Listing 2).
Code Listing 1
Code Listing 2
When I first tried to get things to work I kept on getting this strange error (see Figure1) when I tried to raise an event on my service (see Code Listing 3). I search around and could not find anything on this so I starting thinking about how things were working in WF as I passed a parameter into the workflow. I remembered reading in Presenting Windows Workflow Foundation (Chapter 9) that as you send messages into the workflow they are actually going into a queue which allows for a separation between the actual workflow instance and the host application. I added this knowledge to some more information I gathered about how event arguments are created and how you have to apply the [Serializable] attribute on the class you inherited from WorkflowMessageEventArgs and a light went off in my head. What if I ensured that all of the parameters in my event argument class were serializable. Would this help?
Code Listing 3
In order to really test this I needed to setup a few things. First I coded a few anonymous methods (Code Listing 4) for the workflow events Terminated and Aborted. This allowed me to catch if the workflow was ended in an abnormal way. The one of real concern was the Terminated event so I put some code to walk the exception stack to see what errors were occurring. Next I ran the code again to see if the workflow was terminating and it wasn’t. This at least told me that the workflow was not erroring out and my problem was before the workflow handled the event. So with this in mind I added the [Serializable] attribute to the ComplexObject class (see Code Listing 5) that was used in the LocalServiceEventArgs class (see Code Listing 6). As soon as I did this the code began to work. With this completed I thought my troubles were ended but they were not. In my complex object I also had another embeded object that I was represented as an object. There were some reasons for this that I won’t go into here but the use of this object type opened another issue that again was not very apparent at first. In my workflow I was adding a newly created EmbededObject class instance to my ComplexObject class instance and returning the complex object using the CallExternalMethod activity. When I did this I got the following error (see Figure 2) and my parameter troubles began all over again. To add complexity to the situation at the time I didn’t have an event handler for the Terminate event (which I do in this example and also in my code today) so my application seemed to just hang (and not give me the error shown in Figure 2) which caught me off guard for a few till I added the event handler which I recommend you always implement. So now with the event handler and the message appearing I still was a little confused. Why did the raise event work and not the CallExternalMethod activity? Then I realized what was going on. First I was not populating the EmbededObject in the host only in the workflow so going in was just fine coming back was the issue. I then realized “again” that I did not put the [Serializable] attribute on the EmbededObject inside of the Complex Object. So when I added the [Serializable] attribute to the EmbededObject class things worked fine.
Code Listing 4
Code Listing 5
Code Listing 6
I hope this review of my issues with WF and the process I used to solve the issue helps. At the time these concepts did not seem apparent and I hope that the lessons I learned will not have to be repeated. Since this is Beta technology sometimes the answers to problems are hard to find and sometimes the solutions are not 100% correct so if anyone reading this has any thoughts on this subject please give some feedback. I included the sample application that I used in this blog for reference. Run it the way it is to have the first issue occur. Then uncomment out the [Serializable] line in the ComplexObject class to get the second issue to occur. Finally uncomment out the [Serializable] line in the EmbededComplexObject class to have the application work without any errors. For reference the code is using the Beta2 bits.