0

I created an object of a dynamically created type and i'm attempting to invoke a runtime loaded function.

The invoke call exceptions with: {System.ArgumentException: Object of type 'UserClasses.re_Constituent' cannot be converted to type 'UserClasses.re_Constituent'.}

(yes those 2 classes are the same)

the code for the class, and the code for the invoked function been previously generated and compiled as per https://stackoverflow.com/a/3024112/432976

Does anyone know the source of the error?


The following code exceptions at the magicValue Invoke()

namespace myTest
{
  internal class Program
  {
    Main(){
      //pseudoCode
        BuildTypes();
        {Type myType,MethodInfo myMethod} = factory.getType("re_Constituent");

      //actual code that exceptions
        Node result = (Node) JSON.Deserialize(textstream,myType);
        object magicValue = myMethodInfo.Invoke(null,new object[]{result});
    }
}
namespace UserClasses
{
  public abstract class Node
  {}
}

inspecing when paused at the invoke line of code reveals:

myMethodInfo is: {System.Collections.Generic.List`1[UserClasses.Node] ConvertToCore(UserClasses.re_Constituent)}

the type of the parameter expected by the methodInfo is : "UserClasses.re_Constituent, dynamicGen_reApi_3, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null"

the type passed to the JSON de-serializer (Jil) is listed as: "UserClasses.re_Constituent, dynamicGen_reApi_3, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null"

result.GetType().AssemblyQualifiedName: "UserClasses.re_Constituent, dynamicGen_reApi_3, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null"

re_Constituent is defined in a separate dynamically loaded dll.

namespace UserClasses
{
  public class re_Constituent : Node
  {
    public string id;
    ... other fields
  }
}

the code for the function is found in another runtime loaded dll:

namespace UserClasses
{
  public static class MyExtensions
  {
    public static List<Node> ConvertToCore(this re_Constituent node)
    {
      return new List<Node>()
      {
        new Node(); // actual code is more complex here
      };
    }
  }
}

I noticed something funny with my dynamic generation / loading by iterating all classes in all assemblies loaded, which shows that there were 2 copies of the built assembly in memory:

foreach (Assembly assembly in AppDomain.CurrentDomain.GetAssemblies())
{
    if(!assembly.GetName().Name.StartsWith("System"))
    foreach (Type type in assembly.GetTypes())
    {
        if(type.FullName.Name.StartsWith("System"))
            Console.WriteLine(type+":"+assembly.GetName().Name);
    }
}
Andrew Hill
  • 1,921
  • 1
  • 25
  • 31

1 Answers1

1

When you compile code, the classes are not made available to the general type resolution system until they are loaded. To resolve this issue https://stackoverflow.com/a/3024112/432976 has the code for compiling end with:

    AppDomain.CurrentDomain.Load(results.CompiledAssembly.GetName());
    return results.CompiledAssembly;

The problem is that the assembly read returned from the .Load call is not the same object as the already in-memory assembly from the compilation results.

My problem was that i was examining the returned assembly for types, and caching them for future use, which meant that my version of re_constituent was indeed different from the version of re_constituent that the type system knew about.

To fix this problem, make sure that there is only 1 assembly version exposed to the user code by returning the Loaded version:

 return AppDomain.CurrentDomain.Load(results.CompiledAssembly.GetName());
Andrew Hill
  • 1,921
  • 1
  • 25
  • 31