Is reflection and casting different in .NET 7 vs .NET Framework? I'm porting a project and getting this casting error after moving the code over. The wierd thing is this class implements that interface. This code works in .NET 4.x.
foreach (Assembly pluginAssembly in pluginAssemblies)
{
try
{
// Look for class(s) with our interface and construct them
foreach (var type in pluginAssembly.GetTypes())
{
Type iDesigner = type.GetInterface(typeof(IFireworksDesigner).FullName);
if (iDesigner != null)
{
object instance = Activator.CreateInstance(type); // creates an object
IFireworksDesigner designer = (IFireworksDesigner)instance; // throws an exception
// do stuff
}
}
}
catch(Exception ex)
{
//Something really bad must have happened.
MessageBox.Show("Fatal error reflecting plugins in assembly '" + pluginAssembly.FullName + "'.\r\n" +
"The error message is:\r\n\r\n" + ex.Message);
}
}
Update: I've made a sample repos at https://github.com/chrpai/reflection
With a FW48 EXE calling .NET Standard 2.0 it works. With a Core7 EXE calling either a .NET Standard 2.0 or .NET 7 DLL it fails.
I got the reason, the conclusion is the interface the class implemented and the interface you try to cast to exist in different AssemblyLoadContext
s, so it fails.
The documentation of LoadFile
says:
LoadFile does not load files into the load-from context
That means LoadFile
will load an assembly into a new context, the source code also verifies this.
AssemblyLoadContext alc = new IndividualAssemblyLoadContext($"Assembly.LoadFile({normalizedPath})");
result = alc.LoadFromAssemblyPath(normalizedPath);
After you load LibCore.dll by LoadFile
, there are 2 AssemblyLoadContext
s exist in the program, both of them have an interface IPlugin
with the same name and same assembly name. So the code type.GetInterface(typeof(IPlugin).FullName)
even using AssemblyQualifiedName
is not able to distinguish between different contexts.
Using the following code to verify.
// IndividualAssemblyLoadContext #2
Console.WriteLine(AssemblyLoadContext.GetLoadContext(iDesigner.Assembly));
// DefaultAssemblyLoadContext #0
Console.WriteLine(AssemblyLoadContext.GetLoadContext(typeof(IPlugin).Assembly));
Use LoadFrom
instead of LoadFile
. LoadFrom
loads an assembly into load-from context, so the interface can be shared.
pluginAssemblies.Add(Assembly.LoadFrom(file));
Use an individal core library, for example you can have 3 projects.
ConsoleCoreCore.exe
LibCore.dll
public interface IPlugin
public class Server
Lib1.dll
public class LibStandardPlugin : IPlugin
Both Lib1.dll and ConsoleCoreCore.exe refer to LibCore.dll
ConsoleCoreCore.exe -> LibCore.dll <- Lib1.dll
Now if you load Lib1.dll from LibCore.dll by LoadFile
, Lib1.dll will be loaded into an individal context, but its IPlugin
interface is still the one from LibCore.dll.
// IndividualAssemblyLoadContext #1
Console.WriteLine(AssemblyLoadContext.GetLoadContext(type.Assembly));
// DefaultAssemblyLoadContext #0
Console.WriteLine(AssemblyLoadContext.GetLoadContext(iDesigner.Assembly));
// DefaultAssemblyLoadContext #0
Console.WriteLine(AssemblyLoadContext.GetLoadContext(typeof(IPlugin).Assembly));