Got a strange issue here. We're building a scripting system for our platform using Roslyn and reflection. In order to keep things as concise as possible, this works well for most types but fails for others (I have not been able to see a pattern here).
One of our requirements is that we reference any possible .net assembly so in order to create object instances we have to use the fully qualified assembly names. Normally I use the following helper method to first get the object Type:
This works well enough in most cases, however let's look at the following qualified type string: "Serilog.LoggerConfiguration, Serilog, Version=2.0.0.0, Culture=neutral, PublicKeyToken=24c2f752a8e58a10" (let's call this typeString).
public static Type GetType(string typeName)
{
var type = Type.GetType(typeName);
if (type != null) return type;
foreach (var a in AppDomain.CurrentDomain.GetAssemblies())
{
type = a.GetType(typeName);
if (type != null)
return type;
}
return null;
}
AppDomain.CurrentDomain.GetAssemblies() contains the assembly mentioned above. Using the VS immediate window I can take that particular assembly and try the following (let's call the Serilog assembly asm):
In short, I can find the type by using the FullName but not by using the AssemblyQualifiedName? This doesn't seem to make much sense. Armed with this information, I try some further tests (attempt to locate the Type in the Assembly using text matching):
asm.GetTypes().Where(x=>x.AssemblyQualifiedName == "Serilog.LoggerConfiguration, Serilog, Version=2.0.0.0, Culture=neutral, PublicKeyToken=24c2f752a8e58a10") -> this returns the correct Type.
My two questions regarding this are:
EDIT 1 In order to provide a working repro/example, I've done the following:
static void Main(string[] args)
{
//this is the fully qualified assembly name
string aqn = "Serilog.LoggerConfiguration, Serilog, Version=2.0.0.0, Culture=neutral, PublicKeyToken=24c2f752a8e58a10";
//load assembly from file and insert it into the app domain
Assembly asm = Assembly.LoadFrom("Lib\\Serilog.dll");
AppDomain.CurrentDomain.Load(asm.GetName());
//this is null - fails only if the assembly is loaded using the Assembly.LoadFrom method.
//Otherwise, if it's added to the solution as a nuget package it works.
var direct = Type.GetType(aqn);
//using Linq I can correctly find the Type.
var linq = AppDomain.CurrentDomain.GetAssemblies().Where(x => x.FullName.Contains("Serilog")).FirstOrDefault()
.GetTypes().Where(y=>y.AssemblyQualifiedName == aqn).FirstOrDefault();
}
Finally figured it out. After some digging around it seems to be linked to the load context and needs a custom assembly resolver (more on this on ReflectionTypeLoadException from Roslyn-generated assembly)
In short, by implementing this custom assembly resolver, Type.GetType(AssemblyQualifiedName) works correctly, even for assemblies loaded using Assembly.LoadFrom(). This even works for nested types (defined using qualified assembly names).
One note: Be sure to call the Init() method only once, as otherwise you'll be reseting the assemblies dictionary.