I've been working with a team since March on a huge, complicated billing and customer management system for a utility company. Part of what the application does is manage change orders of various types. Depending on the order's type, it is routed to various locations so that actions can be performed at each of them. When this requirement was originally discussed, it was thought that it would be sufficient to define the paths the orders would go on, statically. That is, the order of stops would always be the same. These paths were allowed to branch, but there was no decision logic involved in them. I looked at WF, at that time, and decided it would be overkill. I then went about writing an engine to manage the process of promoting the orders through the paths which was a huge, messy task.
A few months later, it as decided that decision logic was, in fact, going to be needed in the flows. I reasoned that I had two options at this point. I could either go back into the engine code I had written and implement some polymorphic way to call business logic (such as coding the calls against an Interface and loading up a configured class that implemented the interface at runtime)...OR I could toss the engine and look at workflow, again. I chose to do the latter. I hadn't yet used workflow on any production application, so I armed myself with a couple of books (this one and this one) and dug in.
Now that this portion of the application is rewritten, I wanted to post about my take on the experience.
Less Time and Code
First of all, I was extremely impressed at how much code I DIDN'T have to write for this implementation. In writing my engine, I had to write a ton of code to determine whether or not the order was on a path parallel to the one I was currently processing and whether I could continue or not. Having Workflow handle all that for me was a huge bonus. This also translated into a great deal of time savings. I implemented several complicated flows and had a working system in about two and a half weeks with the previous engine I wrote taking a couple of months.
Expressing Complicated Logic in a Visual Way
I'm not sure why, but I kept thinking I was going to encounter a set of logic I needed to implement that I could not express with the supplied workflow activities, but I did not. I didn't need to write any custom activities (although that would have been a good exercise to have had) in order to get the job done. I found the activities that came in the box to serve me very well.
Along with the good, though, I did encounter a few things that I wasn't crazy about.
Inheritance and the Designer
As I worked through the various flows I had to implement, I kept encountering bits of code tied to code activities that they all the flows had in common. Naturally, my instinct told me to refactor these, put them into one place, and share them. So, I pulled all the common bits into a base class and inserted it into the inheritance hierarchy. I created the base class and had it inherit from SequentialWorkflowActivity (the base class, by default, of a sequential workflow) and then modified my flows to inhert from this base class. What I expected, was for the designer to recognize the inherited methods and allow me to tie my code activities to them. However, the designer did not display any of these methods for me to choose from, and when I manually entered the method name it both insisted upon adding the method to the workflow again AND complained that it could not do so since the class it was inheriting from already had a method by that name. I should mention that I was using VS 2005 for my development and have not tried to do a similar thing in VS 2008. This may have been corrected. Also, there may very well be a way to accomplish what I needed, but the way that seemed obvious to me did not work as I expected it to.
Communication Between Workflow and Host
All communication between the workflow runtime (within which your workflow executes) and the host of the runtime is asynchronous. To communicate into the workflow, you need to fire an event into it and the workflow needs to be in a state of actively listening for that particular event. For the workflow to communicate with it's host, an event is fired from within the flow and the host needs to have a handler for the event. For some of my flows, it was necessary for the flow to occasionally ask the user how to proceed with a messagebox allowing a yes or no answer. The amount of code to implement this was quite a bit more than I expected. I understand that the workflow engine is running on a different thread and that all this communication needs to be synchronized, but I have to think that the API providing this functionality will be improved over time to make quicker work of implementing this type of communication.
You can expect that I'll be posting more details about workflow and my experiences with it along the way. I'm reluctant to share too much at this point since I still consider myself to be a rank amateur with the technology in regard to best practices.