In my project, there is an abstract base class with an abstract method.
We generate implementations based on a schema and later load these by reflection with Assembly.LoadFrom
and then call Assembly.GetType
to get a concrete implementation of an interface which is defined in yet another DLL.
The structure of the different projects (DLL files):
Schema
- Containing type definitionBase
- Has the base class shared by all generated implementationsGenerated
- A generated type that implements the abstract base class fromBase
and the interface fromSchema
.
public interface IExample
{
//Some methods here, irrelevant to the problem
}
public abstract Base
{
protected abstract void SomeMethod(SomeType someArg); //This method is the method the exception specifies, but I suspect it's only because it's the first one in the file.
//More methods like the above, and some non-abstract/virtual methods too
}
public class Generated : Base, IExample
{
protected override void SomeMethod(SomeType someArg)
{
//Some implementation here
}
//More content here, not all of it being from either the interface or the Base type
}
var asm = Assembly.LoadFrom(path);
asm.GetType("SomeNameSpace.Generated"); //This is where it fails
This worked fine until the Base project was updated in an unrelated area and its version advanced.
The generated implementation is being requested by the interface type it implements. (Generics are involved in the type definition, not sure if that's really relevant)
Now normally this would be a simple case of "Oh you just need to re-compile it and include it again" but the exciting part is that this only sometimes fails!
Roughly half the time, it just works. The other half, it throws the TypeLoadException arguing the method doesn't have an implementation. Normally I'd expect it to always fail, but that's not the case.
Of course, including the newly compiled Generated DLL avoids this entirely. But I'm looking to be able to update both the Schema and Base projects without requiring the whole thing. (It's for 'service pack' style software updates only containing the relevant files)
Just to be clear, none of the involved types were modified. No "Oh I just added an optional argument to a method so it's the same method" mistakes.
The only changes are in other parts of the files. Base
is in a big DLL with lots of unrelated utility in it. Base
, IExample
, and the resulting Generated
are still exactly the same. If it was some version resolution causing havoc, I'd expect problems.
This is sadly not a simple small project I could pack up into a reproducible example, but a rather complicated program with many layers and patterns. I'm not sure I could reproduce this if I tried, I'm relying on it failing when the program starts loading things and calling code. (The relevant reflection code that creates an instance of Generated
)
The exception message looks like this: (names changed to match example code, and yes its assembly version is 0.0.0.0)
System.TypeLoadException: Method 'SomeMethod' in type 'SomeNameSpace.Generated' from assembly 'SomeNameSpace.Generated, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null' does not have an implementation. at System.Reflection.RuntimeAssembly.GetType(RuntimeAssembly assembly, String name, Boolean throwOnError, Boolean ignoreCase, ObjectHandleOnStack type) at System.Reflection.RuntimeAssembly.GetType(String name, Boolean throwOnError, Boolean ignoreCase) at SomeMoreOfOurOwn.CallingTheReflection.AndFailing(Factory factory)
As mentioned, this is a case where trying the same thing and hoping for different results works, because this issue doesn't happen half the time. (And not due to the code not being called, the codebase is built on this pattern for everything)
The only predictable thing is that it always fails on the same thing, but I think that's just because it's deterministically doing that one thing first amongst all of the non-updated generated files.
This is a .NET Framework 4.7.1 project.
CodePudding user response:
We found one suspect. Apparently the fact we also load Base
's DLL by reflection via AssemblyResolve
it might cause this issue when there's a version mismatch because it's also directly referenced by another directly referenced assembly.
We've only got tentative proof of this being the matter, but it's the best hunch we've got.
So in conclusion, avoid doing whatever it is we're doing in our project.