We’re I work we rehost the WF Designer in order to allow users of the application to design their own workflows that get executed by our system. At some point we made the decision to go with Xaml only workflows as they are easier to store in a database, and don’t require compiling to an assembly before use. However, certain ways in which the Condition Rule Editor works really damages the user-friendliness of the designer.
Normally when you are adding activities to a workflow these are being promoted to top level properties on the root Activity. So that in the rule editor you can say something like this
this.Activity1.test == 'foo'
This is straightforward enough to be business user friendly (if a bit techie). But with Xaml Only flows, we don’t have the benefit of these properties so we have to write something ugly like
((CustomActivity)this.GetActivityByName("Activity1")).test == 'foo'
And we’ve lost more than just readability here. We are now open to be savaged by runtime errors if we have performed an incorrect cast, or misspelled an activity name.
I’ve been trying for a while to come up with an adequate solution for this. There is a sample in the book Essential WF that shows how you can attach a custom activity code generator to your top level activity which will examine properties on this activity to find out which hard-typed properties it can generate on the activity. This way I could detect when an activity is being added, and modify the root activity in such a way that it would generate code for the properties I desired. Unfortunately, it seems for this to work, you need to call the WorkflowCompiler and generate an actual assembly out to disk. At first, this doesn’t seem so bad, but when analyzed a bit, it is actually quite annoying.
First of all, it seems as if I would have to be constantly flushing the designer and reloading based on the compiled workflow. So this would presumably break down if the workflow had errors. And the name of the assembly will end up in the xaml for the workflow definition, so I can’t just pick any assembly name, I have to pick the same one that will be used later when we attempt to run the workflow (this may be on a different machine). And also, if this workflow gets passivated to the persistence store I need to make sure that I can generate that exact assembly BEFORE I attempt to rehydrate that workflow (again possibly on a different machine). All in all having to generate this assembly adds a lot of problems to using WF, which already had a lot of assembly related complications due to a lack of some kind of formal versioning strategy.
I’ve decided not to even investigate this path currently, as it seems like a bit of a dead end.
Another thing I tried was to hook into the type provider and then dynamically subclass the root workflows to add the hardtyped properties I wanted. Although this seemed to work fine with most of the designer and property editors, the rule editor complained that it didn’t like my reflection emitted dynamic type when I tried to open it. And another idea was shot down in flames. I quite liked this idea, as it didn’t require writing out any actual physical assemblies. But I was concerned how it would interact with passivation and the BinaryFormatter. When I regenned the type before unpassivation would the BinaryFormatter complain that it wasn’t the same type definition?
Overall I feel a bit defeated by WF hosting. There are some very cool scenarios enabled by being able to rehost the WF designer, but there are some very annoying inflexibilities in the design of WF, and the designer itself, which is a bit too mired in old winforms designer paradigms for my liking. Also, as our app is a WPF one, I’m annoyed at having to Crossbow in the designer, but have no choice there. Not for the first time, I’m considering writing my own in WPF.