I am developing a library in C# that generates runtime types using System.Reflection.Emit.TypeBuilder
class and i want to generate the following class hierarchy:
[XmlInclude(typeof(Derived))]
public class Base
{
}
public class Derived : Base
{
}
I use the TypeBuilder class in the following way:
class Program
{
public static void Main(string[] args)
{
var assembly = AppDomain.CurrentDomain.DefineDynamicAssembly(new AssemblyName("Test"), AssemblyBuilderAccess.Run);
var moduleBuilder = assembly.DefineDynamicModule("Test");
var baseTypeBuilder = moduleBuilder.DefineType("Base", TypeAttributes.Public, typeof(Object));
var derivedTypeBuilder = moduleBuilder.DefineType("Derived", TypeAttributes.Public);
derivedTypeBuilder.SetParent(baseTypeBuilder);
baseTypeBuilder.SetCustomAttribute(new CustomAttributeBuilder(typeof(XmlIncludeAttribute).GetConstructor(new[] { typeof(Type) }), new[] { derivedTypeBuilder }));
var baseType = baseTypeBuilder.CreateType();
var derivedType = derivedTypeBuilder.CreateType();
var attribute = baseType.GetCustomAttribute<XmlIncludeAttribute>();
}
}
The call:
var attribute = baseType.GetCustomAttribute<XmlIncludeAttribute>();
I receive the following error:
Could not load file or assembly 'Test, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null' or one of its dependencies. The system cannot find the file specified.
Any ideas are well-appreciated: how can i apply a custom attribute on a TypeBuilder for base class that refers to a TypeBuilder for a derived class?
P.S: I'm using Visual Studio 2017 (v15.7.5) and a C# Class Library (.NET Framework project template) NOT .NET Core or .NET Standard
Cannot provide much reason but a solution that will work without additional load/unload or whatever from and to disk:
Adding a separate field containing the actual Assembly
, one can just subscribe to AppDomain.CurrentDomain.AssemblyResolve
and check if the dynamic assembly was requested.
If it was, you just need to return your field and it works perfectly fine.
Example:
class Program
{
static Assembly ass;
public static void Main(string[] args)
{
AppDomain.CurrentDomain.AssemblyResolve += CurrentDomain_AssemblyResolve;
var assembly = AppDomain.CurrentDomain.DefineDynamicAssembly(new AssemblyName("Test"), AssemblyBuilderAccess.Run);
var moduleBuilder = assembly.DefineDynamicModule("Test");
var baseTypeBuilder = moduleBuilder.DefineType("Base", TypeAttributes.Public, typeof(Object));
var derivedTypeBuilder = moduleBuilder.DefineType("Derived", TypeAttributes.Public);
derivedTypeBuilder.SetParent(baseTypeBuilder);
baseTypeBuilder.SetCustomAttribute(new CustomAttributeBuilder(typeof(XmlIncludeAttribute).GetConstructor(new[] { typeof(Type) }), new[] { derivedTypeBuilder }));
var baseType = baseTypeBuilder.CreateType();
var derivedType = derivedTypeBuilder.CreateType();
ass = baseType.Assembly;
var attribute = baseType.GetCustomAttribute<XmlIncludeAttribute>();
Console.WriteLine(attribute.Type.FullName);
}
private static Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
{
return ass;
}
}
I've reproduced your exception. It's looks like the .NET Framework want to read a module from disk.
So simplest workaround would be to save your assembly to the disk:
var assembly = AppDomain.CurrentDomain.DefineDynamicAssembly(
new AssemblyName("Test"),
AssemblyBuilderAccess.RunAndSave); // allow run & save
var moduleBuilder = assembly.DefineDynamicModule("Test",
"Test.dll"); // specify a file name where module will be stored
...
var baseType = baseTypeBuilder.CreateType();
var derivedType = derivedTypeBuilder.CreateType();
assembly.Save("Test.dll");
Now I was able to get attribute without exception:
var attribute = baseType.GetCustomAttribute<XmlIncludeAttribute>();
Well, I can tell you why the fix @X39 posted works. Stepping through the forest of framework code (perhaps fallbacks for different cultures) for the baseType.GetCustomAttribute(); call, when finally reaching appdomain.cs I see:
At this point, _AssemblyResolve is null (which is the private backing field for AppDomain.CurrentDomain.AssemblyResolve) and after stepping through to return null;, the code crashes with the error you posted.
More info: https://learn.microsoft.com/en-us/dotnet/api/system.appdomain.assemblyresolve?view=netframework-4.7.2
@X39 feel free to merge my answer and let me know so that I can remove mine, I did not want to edit your answer directly.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With