Sunday, October 22, 2006 7:08:34 AM (GMT Standard Time, UTC+00:00)
Harry Pierson was in my WF/WCF course I taught a few weeks ago. He posted on his blog about features he thought were cool in WF here.
One of the ones that I thought was interesting that he caught during my course was that the WorkflowLoaderService is actually a pluggable service (just like every other service except for the WorkflowQueueingService). So of course it came to my mind to show an example of why you might want to create a custom loader service.
The WorkflowLoaderService is a very simple API - it has two methods name CreateInstance both which return an instance of Activity. Everytime a Host calls WorkflowRuntime.CreateWorkflow - the WorkflowLoaderService is called to actually create the instance. One of the CreateInstance methods is for compiled Activities (the one that takes as its argument a Type) and one for XAML activation (the one that takes as its arguments two XmlReaders - one for the workflow and one for rules).
The DefaultWorkflowLoaderService is fairly simple - the CreateInstance that takes a Type uses Activator.CreateInstance to create an instance of the Activity Type. The one that takes XAML is slightly more complex - but essentially uses the WorkflowMarkupSerializer to turn the XAML into an Activity.
So why might you want to replace this service? Well - there are many scenarios - but one that always comes to mind for me resolves around rule loading.
One of the great features of WF is to be able to model some of you logic in rules versus code. In Visual Studio - whenever you add rules, those rules are stored in a .rules file along side your workflow files. These .rules files are then compiled into the assembly as resources. Whenever an Activity first needs a rule, there is an infrastructure that loads the .rules file into a RuleDefinitions type (containing both any RuleSets and RuleConditions) - and stuff the object into a well-known DependencyProperty in the root Activity.
One of the features of rules that is so useful is being able to replace them at runtime with a different set of rules - but with the DefaultWorkflowLoaderService - the only way you can do that is if you use XAML activation. But what if you want to replace rules on a compiled Activity type without having to recompile it. The default infrastructure doesn't allow this.
But - if you build your own WorkflowLoaderService - when a compiled Activity type is requested - you could read the rules from an alternate location (based on configuration or some other algorithm) and then dynamically create the RuleDefinitions and stick it into the Activity using the well-known DependencyProperty. Here is the code that does this in a simulated way (note that you'd have to change the algorithm that loads the alternate rules to something useful):
public class DynamicRuleWorkflowLoader : DefaultWorkflowLoaderService
{
protected override System.Workflow.ComponentModel.Activity CreateInstance(Type workflowType)
{
Activity a = base.CreateInstance(workflowType);
WorkflowMarkupSerializer s = new WorkflowMarkupSerializer();
object o = s.Deserialize(XmlReader.Create("AlternateRules.xml"));
a.SetValue(RuleDefinitions.RuleDefinitionsProperty, o);
return a;
}
protected override Activity CreateInstance(System.Xml.XmlReader workflowDefinitionReader, System.Xml.XmlReader rulesReader)
{
return base.CreateInstance(workflowDefinitionReader, rulesReader);
}
}
WF