I gave a talk at the Greensville Spartanburg Developers Guild last night on Windows Workflow Foundation. The talk covers the basics of WF, including the fact that WF is not basic at all, it has a lot of complexities that have to be mastered to build real applications. There is a lot of power there and it makes sense to use it for workflow oriented enterprise applications, but this is not something you decide to adopt for a couple of conditionals and a loop in your business processing layer.
You can get the slides and demos here: Slides Demos
The more I work with WF, the more comfortable I get with it, but also the more I become convinced that they need a WF-Lite version. There are several key things I highlight in this talk that seem much more complex than they need to be. I understand the reasoning of some of these things, mostly tied to the fact that:
- WF manages workflow scheduling and execution using threads from the thread pool
- WF supports dehydrating your workflow when it is idle, persisting it to a persistence provider (SQL Server supported out of the box), and unloading it from memory
- WF supports logging tracking information to a persistent store to know what workflows/activities are running when and what their state is.
The thing is that not all systems that could benefit from the abstracted design model of WF need these things. But by having these things, it means that certain aspects, particularly communicating with the executing workflow, are much harder than calling from one chunk of code to another in a standard .NET application. If we had a WF-Lite that provided the design time experience (with improvements… see below), but let the app control workflow instantiation and synchronous execution, this technology could apply to even more applications than it will in its current incarnation.
Some of the things that I find people have the hardest time groking are:
1. Presentation of workflow constructs as "Properties". A property is a first class construct of a type in .NET. It has a very precise meaning, as compared to events and methods. In WF, there are a lot of things that are exposed in the designer through the Properties window that are not really properties. They are events or event handler methods that are in your workflow or activities that you are hooking up. Event handlers should show up in the events view to be consistent with other design experiences in VS, and because that is where they belong. Instead, they show up in both the properties view and sometimes in the events view and it makes it confusing as a coder what the heck the designer is creating for you. WF is for developers, so speak the developers lingo dammit.
2. Code Conditions - a bool is just a bool. If you need to hook up an activity that depends on a condition (i.e. IfElse, While, ConditionalActivityGroup, etc.), you should be able to define either a method that returns a bool and point to it, or you should be able to define a bool property and point to it. The model of having to have an event defined that takes a ConditionalEventArgs, hooking up an event handler to that event, and then setting the event argument Result property to true/false just leaves people going "Whahuhhhh????"
3. HandleExternalEvent/CallExternalMethod - The number of things you have to do to conceptually just make a simple method call from the host to the workflow or vice versa is just way too high. I like the fact that the communications are based on interfaces. That part I like from a design perspective – the workflow is sort of a layer unto itself and communicating through an interface is a good way to enforce that separation. However, the number of steps you have to go through to hook up host communication scenarios is just way too high. The calls from the workflow into the host are not too bad, because those just get defined as methods. But the extra steps for the events that provide calls into the workflow just pushes it over the edge.
If you are not familiar with this model, the steps include:
- Define an event argument type to carry parameters (only needed because of the chosen event model - this should change in my opinion) that derives from ExternalDataEventArgs
- Define an interface marked with the ExternalDataExchange attribute
- Define an event on that interface of the type EventHandler<T>, where T is your event argument type
- Hook up the interface and the event to the HandleExternalEvent activity in your workflow that you want to be the call point for the call from the host into the workflow
- Define a class in the host application that implements the interface
- Have a way to fire the event in that class when you want to call into the workflow (a trigger/fire method)
- Register the ExternalDataExchangeService with the runtime when you start it up
- Register an instance of the class that implements the interface with the ExternalDataExchangeService instance that you registered with the runtime
- Finally, trigger the event from the host application at the point where you want to call into the workflow
All of this amounts to what? A simple method call with parameters into the workflow. This is where the attendees jaws usually hit the floor.
Am I wrong here? Isn't this a little more complex than it needs to be for most apps?