In my last post I showed you how the _XamlStaticHelper class uses different semantics when loading assemblies referenced by XAML files.
Today I’m going to show you a solution I’ve built into the Microsoft.Activities library that can help you apply standard CLR behavior when loading referenced assemblies.
Strict Assembly Resolution for Compiled Workflows
Step 1: Download and reference Microsoft.Activities.dll
Download the latest version
Step 2: Create a partial class definition for your compiled workflow
In my example project, I have a compiled workflow named WorkflowCompiled.xaml. I have added a partial class with the same name and .cs extension
Step 3: Create a ReferencedAssemblies static property in your partial class
Add the FullName of any assemblies that you are referencing from your XAML
/// <summary> /// Gets the referenced assemblies. /// </summary> /// <remarks> /// The XamlAppDef build task generates a list of referenced assemblies automatically in the (XamlName).g.cs file /// You can find the list of assemblies and version that need to be referenced there. /// This property returns a simple string list of the assemblies that will cause them to be loaded using the /// standard Assembly.Load method /// </remarks> public static IList<string> ReferencedAssemblies { get { // Create a list of activities you want to reference here var list = new List<string> { "ActivityLibrary1, Version=1.0.0.0, Culture=neutral, PublicKeyToken=c18b97d2d48a43ab", }; // Add the standard list of references list.AddRange(StrictXamlHelper.StandardReferencedAssemblies); return list; } }
Step 4: Create a Constructor in your partial class with a XamlAssemblyResolutionOption argument
The default constructor in your partial class will use the loose assembly loading behavior. You must supply an alternate constructor to get the strict behavior
/// <summary> /// Initializes a new instance of the <see cref="WorkflowCompiled"/> class. /// </summary> /// <param name="assemblyResolutionOption"> /// The assembly Resolution Option. /// </param> public WorkflowCompiled(XamlAssemblyResolutionOption assemblyResolutionOption) { switch (assemblyResolutionOption) { case XamlAssemblyResolutionOption.Loose: this.InitializeComponent(); break; case XamlAssemblyResolutionOption.Strict: StrictXamlHelper.InitializeComponent(this, this.FindResource(), ReferencedAssemblies); break; default: throw new ArgumentOutOfRangeException("assemblyResolutionOption"); } }
Step 5: Construct your workflow using the new constructor passing XamlAssemblyResolutionOption.Strict
WorkflowInvoker.Invoke(new WorkflowCompiled(XamlAssemblyResolutionOption.Strict));
Result
Your compiled workflow will now behave like any other CLR object and construct successfully if and only if it can resolve the specific version of all referenced assemblies. If it cannot locate the assembly file it will throw a FileNotFoundException and if it can locate the file but it is not the correct version or public key token it will throw a FileLoadException.
Strict Assembly Resolution for Loose XAML
Loose XAML is an activity loaded from a XAML file using ActivityXamlServices.Load(). Unless the XAML file has FullName references (which it does not by default) ActivityXamlServices.Load will load with a partial name. This means it could load any assembly it finds with a matching name without regard to the version or public key token.
There are two ways to fix this.
- You can use the <qualifiedAssembly>configuration element to specify the FullName of the assembly you want to use
- Use Microsoft.Activities.StrictXamlHelper.ActivityLoad()
Step 1: Download and reference Microsoft.Activities.dll
Download the latest version
Step 2: Create a list of referenced assemblies
public static IList<string> GetWorkflowLooseReferencedAssemblies() { // Create a list of activities you want to reference here var list = new List<string> { "ActivityLibrary1, Version=1.0.0.0, Culture=neutral, PublicKeyToken=c18b97d2d48a43ab", }; // Add the standard list of references list.AddRange(StrictXamlHelper.StandardReferencedAssemblies); return list; }
Step 3: Load the activity using StrictXamlHelper.ActivityLoad()
// This will ensure the correct assemblies are loaded prior to loading loose XAML var activity = StrictXamlHelper.ActivityLoad( "WorkflowLoose.xaml", GetWorkflowLooseReferencedAssemblies()); WorkflowInvoker.Invoke(activity);
Summary
Loading the correct version of a referenced assembly is vital. The default behavior of both compiled workflows and loading loose XAML may result in unexpected behavior due to loading a newer version of the referenced assembly.
I recommend that you use StrictXamlHelper to ensure that you load the expected version of referenced assemblies.