How do I load a WF4 workflow from XAML? Important detail: The code that loads the workflow shouldn't need to know beforehand which types are used in the workflow.
I am having a very hard time loading a WF4 workflow from the XAML file create by Visual Studio. My scenario is that I want to put this file into the database to be able to modify it centrally without recompiling the Workflow invoker.
I am currently using this code:
var xamlSchemaContext = new XamlSchemaContext(GetWFAssemblies());
var xmlReaderSettings = new XamlXmlReaderSettings();
xmlReaderSettings.LocalAssembly = typeof(WaitForAnySoundActivity).Assembly;
var xamlReader = ActivityXamlServices.CreateBuilderReader(
new XamlXmlReader(stream, xmlReaderSettings),
xamlSchemaContext);
var activityBuilder = (ActivityBuilder)XamlServices.Load(xamlReader);
var activity = activityBuilder.Implementation;
var validationResult = ActivityValidationServices.Validate(activity);
This gives me a whole lot of errors, which fall into two categories:
Category 1:
Types from my assemblies are not known, although I provided the correct assemblies to the constructor of XamlSchemaContext
.
ValidationError { Message = Compiler error(s) encountered processing expression "GreetingActivationResult.WrongPin". 'GreetingActivationResult' is not declared. It may be inaccessible due to its protection level. , Source = 10: VisualBasicValue, PropertyName = , IsWarning = False }
This can be solved by using the technique described here, which basically adds the assemblies and namespaces of all used types to some VisualBasicSettings
instance:
var settings = new VisualBasicSettings();
settings.ImportReferences.Add(new VisualBasicImportReference
{
Assembly = typeof(GreetingActivationResult).Assembly.GetName().Name,
Import = typeof(GreetingActivationResult).Namespace
});
// ...
VisualBasic.SetSettings(activity, settings);
// ... Validate here
This works but makes the whole "dynamic loading" part of the Workflow a joke, as the code still needs to know all used namespaces.
Question 1: Is there another way to get rid of these validation errors without the need to know beforehand which namespaces and assemblies are used?
Category 2:
All my input arguments are unknown. I can see them just fine in activityBuilder.Properties
but I still get validation errors saying they are unknown:
ValidationError { Message = Compiler error(s) encountered processing expression "Pin". 'Pin' is not declared. It may be inaccessible due to its protection level. , Source = 61: VisualBasicValue, PropertyName = , IsWarning = False }
No solution so far.
Question 2: How to tell WF4 to use the arguments defined in the XAML file?
Question 2: You can´t execute an ActivityBuilder, it´s just for design. You have to load a DynamicActivity (only through ActivityXamlServices). It should work that way (without using a special XamlSchemaContext), but you must have loaded all used assemblies in advance (placing them in the bin directory should also work, so far about Question 1, DynamicActivity might make things a little bit easier):
var dynamicActivity = ActivityXamlServices.Load(stream) as DynamicActivity;
WorkflowInvoker.Invoke(dynamicActivity);
In general, I got the impression that you´re trying to implement your own "ActivityDesigner" (like VS). I tried this myself, and it was quite hard to deal with DynamicActivity and ActivityBuilder (as DynamicActivity is not serializable but ActivityBuilder cannot be executed), so I ended up with an own activity type that internally converts one type into the other. If you want to have a look at my results, read the last sections of this article.