Personally, this would be a "buy before build" decision for me. I'd buy something before I'd write it.
I work for a company that's rather large and can be foolish with its money, so if you're writing this for yourself and can't buy something I'll retract the comment.
Here are a few random ideas:
I'd externalize the workflow into a configuration that I could read in on startup, maybe from a file or a database.
It'd look something like a finite state machine with states, transitions, events, and actions.
I'd want to be able to plug in different actions so I could customize different flows on the fly.
I'd want to be able to register different subscribers who would want to be notified when a particular event happened.
I wouldn't expect to see anything as hard-coded as that e-mail server. I'd rather encapsulate that into an EmailNotifier that I could plug into events that demanded it. What about a beeper notification? Or a cell phone? Blackberry? Same architecture, different notifier.
Do you want to include a handler for human interaction? All the workflows that I deal with are a mix of human and automated processing.
Do you anticipate wanting to connect to other systems, like databases, other apps, web services?
It's a tough problem. Good luck.
It turned out that the TypeProviders are not a reliable way to resolve assembly references that are missing for the workflow rendering (and accessing the tracking information for old activities etc.). The problem is not only that they don't work for SqlTrackingService, but also that to be able to pass an assembly into the TypeProvider it has to be loaded into the AppDomain. Having multiple versions of the same assembly in the AppDomain causes crazy things to happen, so it was absolutely necessary to avoid it.
Although it is not possible to unload an assembly from an AppDomain once it's been loaded, it is possible to create an isolated AppDomain which loads the older version of assemblies when necessary (I've done this registering to the AssemblyResolve event of the AppDomain) and then simply unload the whole AppDomain when the work is done. For example of how this can be done see related question.
Now I'm happily showing diagrams from different versions of activity libraries without any problems ;)
Best Answer
Achieving your goal here is likely to be quite tricky however lets start with the easy bit:-
You can reconstruct a workflow from XOML using the
WorkflowMarkupSerializer
found in theSystem.Workflow.ComponentModel.Serialization
namespace.Similarly you could reconstruct a "snippet" of activities held in something that inherits from CompositeActivity using the
CompostiteActivityMarkupSerializer
.However, to integrate the new root activity into the currently running workflow requires more work. You need to use an instance of the
WorkflowChanges
class to make the new activity by modifing the Workflow definition used by the current instance.Now the documentation is some what sketchy and even a little evasive on this subject. Two important points can be gleaned though:-
ApplyWorkflowChanges
is needed and this member hasprotected
accessibility.Hence we can deduce that we will need a custom root activity to at least assist in this requirement.
There are probably more ways that this could be structured but lets assume we have a
SequenceActivity
in which we have a custom "InvokeWorkflow" activity performing the workflow modification and we intend to place the resulting new activity at the end this containing sequence.First we'll need an interface definition which we can implement on the custom root activity:-
In our custom root activity we would implement this interface explicitly:-
In the
Execute
method of the custom "InvokeWorkflow" activity:-Note I haven't actually tested any of this, its cobbled together from crumbs of info buried in the documentation.