XamlInjector–Cannot create unknown type errors

If you are using Microsoft.Activities.UnitTesting.XamlInjector to mock activities you may run into an error like this.

 Test method XamlInjectorRepro.Test.UnitTest1.TestMethod1 threw exception: 
System.Xaml.XamlObjectWriterException: Cannot create unknown type '{clr-namespace:XamlInjectorRepro}MyCustomActivity'.

This error occurs when the XAML loader encounters a type that was declared in the same assembly as the XAML when it was created.  For example, I have project where MyWorkflow.xaml and MyCustomActivity.cs are in the same project

image

I have created a unit test where I am going to mock MyCustomActivity

    1: [TestMethod]
    2: [DeploymentItem(@"XamlInjectorRepro\MyWorkflow.xaml")]
    3: public void TestMethod1()
    4: {
    5:     var xamlInjector = new XamlInjector("MyWorkflow.xaml");
    6:     xamlInjector.ReplaceAll(typeof(MyCustomActivity), typeof(FakeMyCustomActivity));
    7:  
    8:     var activity = xamlInjector.GetActivity();
    9:  
   10:     var host = WorkflowInvokerTest.Create(activity);
   11:     try
   12:     {
   13:         host.TestActivity();
   14:     }
   15:     finally
   16:     {
   17:         host.Tracking.Trace();
   18:     }
   19: }

When I run this test it fails with the exception shown.

Why?

    1: xmlns:local="clr-namespace:XamlInjectorRepro" 

The failure occurs because the XAML is making an assumption that the local: namespace refers to the currently executing assembly.  That was true when the XAML is run in the context of the project it was created in but now when unit testing it is no longer true.  This problem occurs not only with XamlInjector but any time when you load XAML and execute it outside of the context in which it was created.

 

Solution

With XamlInjector the solution is simple.  We have provided a constructor overload that you can use to supply an assembly to stand in for the “local” assembly.  This constructor stores the local assembly and then supplies a XamlXmlReader with the overridden local assembly.

 

    1: internal XamlXmlReader GetAppropriateXamlXmlReader(XmlReader reader)
    2: {
    3:     if (this.overrideLocalAssembly == null)
    4:     {
    5:         return new XamlXmlReader(reader);
    6:     }
    7:     else
    8:     {
    9:         var readerSettings = new XamlXmlReaderSettings { LocalAssembly = this.overrideLocalAssembly };
   10:         return new XamlXmlReader(
   11:             reader, new XamlSchemaContext(new[] { this.overrideLocalAssembly }), readerSettings);
   12:     }
   13: }

To use it, just make one small change to the test code above (line 6) to supply the assembly where your custom activity lives.

 

    1: [TestMethod]
    2: [DeploymentItem(@"XamlInjectorRepro\MyWorkflow.xaml")]
    3: public void TestMethod1()
    4: {
    5:     // Pass the assembly of MyCustomActivity to the ctor as the local assembly
    6:     var xamlInjector = new XamlInjector("MyWorkflow.xaml", typeof(MyCustomActivity).Assembly);
    7:     xamlInjector.ReplaceAll(typeof(MyCustomActivity), typeof(FakeMyCustomActivity));
    8:  
    9:     var activity = xamlInjector.GetActivity();
   10:  
   11:     var host = WorkflowInvokerTest.Create(activity);
   12:     try
   13:     {
   14:         host.TestActivity();
   15:     }
   16:     finally
   17:     {
   18:         host.Tracking.Trace();
   19:     }
   20: }