I am trying to implement an activity similar to InvokeWorkflow, which could dynamically load a XOML file, instantiate an activity tree from it, and use it as its only child.
This would be similar to InvokeWorkflow except that the activities which are dynamically loaded are inlined into the main workflow (which is better from a monitoring perspective).
I looked at XamlReader as a potential way of doing this, but apparently it is not su开发者_StackOverflow中文版itable for loading workflows (only UI stuff).
Thanks, Julien
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 the System.Workflow.ComponentModel.Serialization
namespace.
var serializer = new WorkflowMarkupSerializer();
object root = serializer.Deserialize(myXmlReader);
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:-
- Ultimately a call to
ApplyWorkflowChanges
is needed and this member hasprotected
accessibility. - The documentation indicates that this needs to occur on the root activity of a workflow.
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:-
internal interface IModifiableWorkflow
{
void ApplyWorkflowChanges(WorkflowChanges workflowChanges);
}
In our custom root activity we would implement this interface explicitly:-
public class CustomSequentialActivity : SequentialWorkflowActivity, IModifiableWorkflow
{
void IModifiableWorkflow.ApplyWorkflowChanges(WorkflowChanges workflowChanges)
{
base.ApplyWorkflowChanges(workflowChanges);
}
}
In the Execute
method of the custom "InvokeWorkflow" activity:-
// Get root activity
var root = this.Parent;
while (root.Parent != null) { root = root.Parent; }
// Create an instance of WorkflowChanges based on the root activity
var changes = new WorkflowChanges(root);
//Find the parent sequence activity in the transient workflow definition
var target = changes.TransientWorkflow.GetActivityByName(this.Parent.Name);
Activity newActivity = YourCodeToLoadActivityDetailsFromXoml();
target.Activities.Add(newActivity);
//Apply the new changes
((IModifiableWorkflow)root).ApplyWorkflowChanges(changes);
Note I haven't actually tested any of this, its cobbled together from crumbs of info buried in the documentation.
Thanks so much Anthony.
I have to say that your dynamic workflow modification is cool, but it was a little scary. I ended up composing workflows using a modification of Jon Flander's CallWorkflowActivity.
Some tricks I learned with XOML-only workflows loaded at runtime (using WF 3.5):
remove x:Class attribute inside the XOML
delete the code-behind file
for the VS designer to work, those XOML files need to be separated in their own projects (no code, such as base activities or common types, in the project where the XOML is located)
mark the XOML as Content in VS and Copy Always so it is placed with your binaries
even so, VS 2008 usually needs a full Rebuild in order to properly copy newly modified XOML files...
you may need to set breakpoints manually, as explained here
精彩评论