We left off in Part I with a contract, an event args class, and all the metadata we need to raise events to a workflow. As Captain Hazelwood once said, what could possibly go wrong?
Let's start with an implementation of our payment processing contract. The code has a subtle problem, and will cause an exception.
The exception is an EventDeliveryFailedException, and the Message property will read like "Event PaymentProcessed on interface type IPaymentProcessingService for instance ID [GUID] cannot be delivered". The message doesn't yield any obvious clues, and we need to dig deeper to find more information.
If we look at the InnerException property, we get closer to an answer. The inner exception is an InvalidOperationException with the Message of "EventArgs not serializable". This exception is a little confusing, because we did make our EventArgs serializable! Notice in the dialog below, the current exception ($exception in the 2005 debugger) is wrapping yet another inner exception, with the precise cause of failure.
The value of the message is curt off, but says "Type PaymentProcessingService is not marked as serializable". It appears every parameter going into the event must be serializable, including the sender parameter. We pass a this reference, which points to our payment processing service. The workflow instance doesn't actually need a reference to our payment service (if it needs to invoke a method on the service, it can use the CallExternalEventActivity), so we can fix this problem by leaving the sender parameter as null or Nothing.
If you are seeing an event delivery failure, drill into the inner exceptions to find the exact type causing the problem. You event args might contain an object graph with an non-serializable type inside.
I did jump ahead just a bit, because before the event will even throw an exception we need to configure the workflow runtime to handle events from our service. We do this by layering the ExternalDataExchangeService into the workflow runtime. The ExternalDataExchangeService manages the host's local communication services, like our payment processing service.
Here is a version of the above code with a bug that took me quite some time to track down.
We have to add our service to the ExternalDataExchangeService, and not directly to the workflow runtime. My service would fire an event, but nothing would happen. Looking at the event in the debugger showed a null value, meaning nobody was subscribing to the event. It's the ExternalDataExchangeService that reflects on the incoming service, looking for metadata like the ExternalDataExchange attribute, and subscribing to events.
Raising events to a workflow can be finicky. If the events appear to fire into the empty vacuum of space, make sure the ExternalDataExchanceService and the local communications services are properly configured and added to the runtime. If the workflow runtime is throwing exceptions, the exception will most likely wrap the juicy details in its InnerException property (which in turn might have an InnerException). Hopefully, these two tips can save someone some time.