I have a service that involves downloading an assembly from cloud storage, creating an instance of it using Activator.CreateInstance and then invoking a method on it.
I have set up a AssemblyResolve method to download dependencies which works fine, but to test/experiment I am now trying to manually download assemblies. I have got as far as finding which dependencies are needed, downloading them and then loading them using
Assembly.Load(byte[])
After which I can see they are loaded into the AppDomain via
AppDomain.CurrentDomain.GetAssemblies())
However when I am invoking the method on the assembly which references this, it still goes to the AssemblyResolver.
I may be misunderstanding how loaded assemblies and the AppDomain works but it seems to me that once the assembly is loaded it should be available to this assembly and it shouldn't need to resolve it?
Why can't it "see" it? The version and name etc is the same.
I have read about the different assembly binding contexts here and I think this could be the issue? It suggests that using Assembly.Load(string) will load to a different context than Assembly.Load(byte)? In which case how do I do this when I just have the assembly in memory as a byte[]?
Thanks
You need to obtain the Type directly from the assembly you loaded, as it is not loaded into the correct context.
var assembly = Assembly.Load(File.ReadAllBytes(some_path));
// This will work. Note that you don't need the assembly-qualified name,
// as you are asking the assembly directly for the type.
var type1 = assembly.GetType("My.Special.Type");
// This will not work - the assembly "My.Assembly" is not loaded into
// the Load context, so the type is not available.
var type2 = Type.GetType("My.Special.Type, My.Assembly");
In the code above, type1 will have a reference to the Type, but type2 will be null, as the assembly is not loaded into the normal Load context.
I am using the following code to load dlls at run time, and store their classes for later use.
public LoadDll(byte[] data)
{
Assembly loadedAssembly = Assembly.Load(data);
System.Type[] types = loadedAssembly.GetTypes();
TypeRepo.Register(types);
}
and this works great, but if the dll I built has reference to another dll I get the error "The classes in the module cannot be loaded." when calling GetTypes().
How can I provide a specific file path to allow the loaded assembly accesses to a dependency on disk?
You should play with AppDomain.AssemblyResolve event
See what remarks section of the linked documentation points out:
It is the responsibility of the ResolveEventHandler for this event to
return the assembly that is specified by the ResolveEventArgs.Name
property, or to return null if the assembly is not recognized. The
assembly must be loaded into an execution context; if it is loaded
into the reflection-only context, the load that caused this event to
be raised fails.
So you need to perform an Assembly.LoadFrom to return the whole Assembly instance by loading satellite assemblies from an arbitrary path defined by you in code.
I am working on a solution that has a set up like this: We have assembly A which performs some functions, and assembly B which is purely domain models that are used by assembly A (and other assemblies). Both are stored in Azure and are downloaded for use when required by the solution.
Assembly A is downloaded and loaded fine, and I have created a ResolveEventHandler to ensure that Assembly B is downloaded when required and I've added it by doing:
AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(AssemblyResolveFunc);
The resolver works fine but the issue is it is only called once, regardless of the outcome. If Assembly B is found then it loads it and all is well, however if Assembly B has not yet been uploaded and can't be found then it fails as expected, but it will never try again. On the next call it will simply throw an error and will not try to resolve the dependency.
I feel like I'm not understanding the way ResolveEventHandlers are meant to be used, but until assembly B is loaded correctly, should it not retry each time?
Yes, you have only got a single change resolving assemblies. This operation is quite expensive, so it won't try it over and over again.
What might help is to manually walk down the referenced assemblies and download and load those assemblies manually afterwards.
I am writing a test app to perform some regression tests. The idea is to be able to run test over multiple versions of a library. My goal is to load the dlls up in a Dictionary where the key is the version string (such as "3.0.0.0") and the value is the Assembly instance. I am able to dynamically load one assembly and call a method on it, but when I try to load a second one, I get the following exception:
The located assembly's manifest definition does not match the assembly reference.
I am loading the assemblies with the following line:
asm = Assembly.LoadFrom(lib, hash, System.Configuration.Assemblies.AssemblyHashAlgorithm.MD5);
'lib' is the complete filename and path of the dll.
'hash' is the md5 sum of the dll.
I looks like even though I am telling Windows "use this dll", it looks at the name and says "I already have that one" and uses the previously loaded one and since the hash doesn't match, it fails. Originally, the dlls being loaded did not have an Assembly Version set, so I set it on 4 different versions, but it still threw the same exception.
What is the fix for this?
Jordon
You cannot load more than one version of the same assembly into single AppDomain. Also once loaded, assembly cannot be unloaded from AppDomain (with exception of dynamically created transient assemblies in .NET 4), but it is possible to unload whole AppDomain (which unloads all assemblies, that were loaded in it). Therefore you must load each version of your assembly into separate (newly created) AppDomain. Also be very careful to NOT pass any reference to loaded assembly between individual AppDomains (and especially to main AppDomain, where your testing app resides), because otherwise .NET will try to load assembly into every AppDomain, where reference to this assembly appears and you will end up with the same error again.
You'll need to the assemblies into separate AppDomains.
I have a solution containing several projects.
There is one project that references all other projects, there may be references between these referenced projects as well.
Let's assume project A as the main one.
Project B has a MyType class.
Project C has an extension method for MyType.
Each assembly has an attribute. It allows me to determine if the assembly is mine or an external library (maybe it's not the best way to do it, but it works).
At some point in A I have a mechanism that uses reflection to find all extension methods of MyType. To do that I handle event AppDomain.AssemblyLoad to create a list of my loaded assemblies (marked with an attribute). Then I can scan the assemblies for proper extension methods.
Unfortunately, at this moment C is not loaded, because nothing from C has been called. Thus, the extension is not found.
I've tried using Assembly.GetExecutingAssembly().GetReferencedAssemblies(), but I think it returns only loaded assemblies, no luck here.
My final solution in A is:
public static void Main(string[] args)
{
TypeFromProjectC temp;
//further code...
}
It works because the assembly gets loaded on temp declaration, but...
I could try to load assemblies by path/name, but let's assume it's not an option. I want to create a generic solution that would not need to be altered each time I add another reference. In fact that is why I have added this attribute for my assemblies.
Something like GetReferencedAssemblies() would be great. It would be the best if I could check the assembly for my attribute before calling Load(), but it's not that necessary. (Does it even makes sense? When I think of it now, I'd guess that it must be loaded before the attributes are available, but maybe some meta information could do?)
It's the first time I'm playing with multiple assemblies with quite a complicated structure, so it's possible that I don't really have a clue how does it work and my question is plainly stupid.
I hope it's not ;]
Some notes and comments before my answer:
1) You have a reference to ClassLibrary1 and ClassLibrary2, and instantiate Class1 from ClassLibrary1. In that case compiler throws away reference to ClassLibrary2 and Assembly.GetReferencedAssemblies() returns to you actual optimized list of references. When you use any type from ClassLibrary2, complier will respect that reference also. The best way for making strong refs is:
static class StrongReferencesLoader
{
public static void Load()
{
var ref1 = typeof(Class1); // ClassLibrary1.dll
var ref2 = typeof(Class2); // ClassLibrary2.dll
}
}
This allows to reference static classes also and this allows to force loading of every strong-referenced library by calling this method.
2) You do use an AppDomain.AssemblyLoad event. Consider subscribing here from your entry point. Be careful, because if your entry point make something else this can lead to loading assembly before subscribing occurs.
class Program
{
static void Main()
{
AppDomain.CurrentDomain.AssemblyLoad += CurrentDomain_AssemblyLoad;
new Class1();
}
static void CurrentDomain_AssemblyLoad(object sender, AssemblyLoadEventArgs args)
{
Console.WriteLine("! " + args.LoadedAssembly.FullName);
}
}
in this sample, you will not get an output about loading ClassLibrary1, because loading of library was performed before executing main method. Runtime loads all necessary libraries for executing specified method. If we extract this call to another method - everything become fine.
class Program
{
static void Main()
{
AppDomain.CurrentDomain.AssemblyLoad += CurrentDomain_AssemblyLoad;
RealMain();
}
static void RealMain()
{
new Class1();
}
static void CurrentDomain_AssemblyLoad(object sender, AssemblyLoadEventArgs args)
{
Console.WriteLine("! " + args.LoadedAssembly.FullName);
}
}
In that case, Main is executed without problem, and subscribes immediately. After that, assembly loading performed due to call RealMain that contain a byte-code referenced to ClassLibrary1, and your event handler works.
3) Note that in any time you can list all loaded assemblies by AppDomain.CurrentDomain.GetAssemblies()
I'm not fully understand your problem. Is that only solution-wide analysis, or generic idea for future plugins, created by any one else? Solution-wide analysis can be solved in compile-time or run-time, but open plugin system is only runtime.
Run time solution
First of all, you should understand that it is impossible to detect every plugins. Let say, Class1 have extensions, written by some body for another application and installed in another directory:
some pseudo-structure:
\GAC\ClassLibrary1.dll
\ProgramFiles\MyApp\ConsoleApplication1.exe // our entry point
\ProgramFiles\SomeApp\ClassLibrary3.dll // some library from another solution, that references shared ClassLibrary1
You can detect plugins in your GAC, AppDomainBase path, RelativeSearchPath (all of that done by built-in assembly resolver), any custom hardcoded path, and paths of already loaded assemblies AppDomain.CurrentDomain.GetAssemblies().Select(x => Path.GetDirectoryName(x.Location)).Distinct();
And you can analyse that paths recursively.
But you'l never find path like \ProgramFiles\SomeApp\ClassLibrary3.dll unless you scan entire computer.
I hope, that searching in deep to already loaded assemblies location can solve your problem.
Note that you can use Mono.Cecil, but it is convenient to use Assembly.ReflectionOnlyLoad for that purpose. You can load by file name using Assembly.ReflectionOnlyLoadFrom, and you can help resolve assemblies in another location by AppDomain.CurrentDomain.ReflectionOnlyAssemblyResolve event. But you can not use that for custom attributes analysis. In that case some hack is required, like special class name (static class MySuperMark{}), which existance can be checked with easy. It is less hacky to use special interface that every plugin required to implement (but it is not so close to your case).
Compile time solution
Only for solution-wide analysis.
Since you have noted, that
I want to create a generic solution that would not need to be altered each time I add another reference.
Probably you are looking for solution-wide answer. I can suggest some metaprogramming here. Usually I'm solving such task by metacreator http://code.google.com/p/metacreator/. You can run some piece of code before compilation of ConsoleApplication1. For example, you can create a class holding strong references for every referenced dlls automatically by metacode and you can run such class for ensuring early loading of referenced assemblies. This class will actualized on every compilation automatically.
Please, describe more details, which case I can describe more deeply.
In my opinion, assembly analysis with Mono.Cecil is useful here. For example you can enumerate references (of course without ones deleted from project by compiler) without loading given assembly and any of its references by:
var assembly = Mono.Cecil.AssemblyDefinition.ReadAssembly("protobuf-net.dll");
foreach(var module in assembly.Modules)
{
foreach(var reference in module.AssemblyReferences)
{
Console.WriteLine(reference.FullName);
}
}
Such code prints:
mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
System.Xml, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
System.ServiceModel, Version=3.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
System.Runtime.Serialization, Version=3.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
However IMO better idea is to scan through directory, possibly containing plugins and check them for your attribute using Cecil and therefore without loading (because you do not know at that point whether you want to load them at all):
var assembly = Mono.Cecil.AssemblyDefinition.ReadAssembly("protobuf-net.dll");
foreach(var attribute in assembly.CustomAttributes)
{
Console.WriteLine(attribute.AttributeType.FullName);
}
There are couple options for comparing your attribute (which is a class from loaded assembly) to Cecil's attribute description, full type name is one of them.
Assembly.GetReferencedAssemblies() gives you a very reliable list of assemblies that your code actually uses. You will have to recurse through the list so you will find all of them, not just the ones that you main .exe assembly uses.
What you'll get does not necessarily match the assemblies you added with Project + Add Reference. The compiler makes sure to only include referenced assemblies in the metadata that your code actually uses. Which is perhaps why TypeFromProjectC temp; works.
If there are other assemblies lying around that ought to be loaded but are not found by recursing through GetReferencedAssemblies() then you have to load them yourself explicitly with Assembly.Load/From(). Actually using types from such assemblies has to be done manually too, using Activator.CreateInstance() to create an object and Reflection to call methods etcetera.
There's no evidence in your question that you actually intend to use Reflection, especially because you mention extension methods. An extension method that actually gets used in your code will always end up producing an assembly that's discovered through ReferencedAssemblies(). It is also fairly unclear why you want to do this in the first place. The just-in-time compiler is already very good at loading assemblies on demand, doing so when it compiles code just in time. Which is almost always desirable since it turns a long startup delay you'd get by forcing the CLR to find all assemblies into a hundred needle pricks that the user won't notice. Lazy assembly loading is very much a feature, not a bug.
If you don't have a static, compile-time reference to an assembly, directly or indirectly, such an assembly is not going to get loaded and thus is invisible to your enumeration logic. It is possible, however, to dynamically load all assemblies in, say, a particular directory at run-time.
On the other hand, I don't believe there is a place for a system-global dictionary of assemblies that might have a particular custom attribute, which seems to be what you're asking for.
You are looking for your ext methods so the referenced assemblies can be found int the following locations:
the sub directories of the executing asm - simple to implement
sub directories of called asms - simple to implement using the assembly loaded event
mainfest - you have to figure this one out but I dont think it is that hard. Just parse the xml and read the elements
GAC - if you register your asms in the GAC. The solution can be found here:Extract assembly from GAC