C# Assembly.Load vs Assembly.ReflectionOnlyLoad
回答1
As per Jon's reply, it would be helpful to know what's in LoaderExceptions
. In lieu of this information, I think I can hazard a guess. From MSDN:
If the assembly has dependencies, the ReflectionOnlyLoad method does not load them. If you need to examine them, you must load them yourself.
You need to attach a handler to AppDomain.ReflectionOnlyAssemblyResolve
to help the CLR load any dependencies of the assembly you're loading. Have you done this?
回答2
The ReflectionOnly methods are the only way you can load a specific Assembly on disk to examine without going via the usual Load/LoadFrom rules. For example, you can load a disk-based assembly with the same identity as one in the GAC. If you tried this with LoadFrom or LoadFile, the GAC assembly is ALWAYS loaded.
Additionally, you may not call GetCustomAttributes(...) on the return Assembly instance since this will attempt to instantiate the Attributes on the assembly, which are ReflectionOnly. You must use CustomAttributeData class's static methods for this.
No types in an assembly loaded via ReflectionOnly may be instantiated.
Assembly.ReflectionOnlyLoadFrom(assemblyPath).GetName() VS Assembly.LoadFrom(assemblyPath).GetName()
When you load an assembly for ReflectionOnly
, only the metadata gets loaded. This allows you to inspect its types, but not instantiate or execute any of them.
There's also a property indicating whether an assembly was loaded for reflection only.
So per AppDomain, an assembly can be loaded once: either fully, or for reflection only. Given it's already fully loaded, you can't load it again for reflection only.
The call to Assembly.LoadFrom()
, even when provided two different paths, will only load the same assembly once, as long as they match in version. See also Side effects of calling Assembly.Load multiple times.
See also: MSDN: How to: Load Assemblies into the Reflection-Only Context.
Assembly.Load and Assembly.LoadFrom differences?
Assembly.Load and Assembly.LoadFrom uses different loading contexts. Assembly.Load loads assembly in the context of host assembly (default context). For correct loading all the dependant assemblies should be avaliable from host application path, GAC or should be loaded manually in advance.
Assembly.LoadFrom does't use host assembly context. It creates own loading context (load-from context) and able to automatically resolve depandant assemblies not present in the default context. These assemblies from a path that is not under the host application path.
It seems your default styles requires assemblies from a path that is not under the host application path.
Difference between LoadFile and LoadFrom with .NET Assemblies?
回答1
Does this clear it up?
// path1 and path2 point to different copies of the same assembly on disk:
Assembly assembly1 = Assembly.LoadFrom(path1);
Assembly assembly2 = Assembly.LoadFrom(path2);
// These both point to the assembly from path1, so this is true
Console.WriteLine(assembly1.CodeBase == assembly2.CodeBase);
assembly1 = Assembly.LoadFile(path1);
assembly2 = Assembly.LoadFile(path2);
// These point to different assemblies now, so this is false
Console.WriteLine(assembly1.CodeBase == assembly2.CodeBase);
Edit: to answer the questions you raised in your revised question, you definitely want to read Suzanne Cook on Assembly Identity.
There are a lot of rules that govern how assemblies are loaded, and some of them have to do with how they resolve dependencies - if your AssemblyA is dependent on AssemblyB, where should .NET look to find AssemblyB? In the Global Assembly Cache, the same directory it found AssemblyA, or somewhere else entirely? Furthermore, if it finds multiple copies of that assembly, how should it choose which one to use?
LoadFrom
has one set of rules, while LoadFile
has another set of rules. It is hard to imagine many reasons to use LoadFile
, but if you needed to use reflection on different copies of the same assembly, it's there for you.
回答2
From Suzanne Cook's blog:
LoadFile vs. LoadFrom
Be careful - these aren't the same thing.
LoadFrom() goes through Fusion and can be redirected to another assembly at a different path but with that same identity if one is already loaded in the LoadFrom context.
LoadFile() doesn't bind through Fusion at all - the loader just goes ahead and loads exactly* what the caller requested. It doesn't use either the Load or the LoadFrom context.
So, LoadFrom() usually gives you what you asked for, but not necessarily. LoadFile() is for those who really, really want exactly what is requested. (*However, starting in v2, policy will be applied to both LoadFrom() and LoadFile(), so LoadFile() won't necessarily be exactly what was requested. Also, starting in v2, if an assembly with its identity is in the GAC, the GAC copy will be used instead. Use ReflectionOnlyLoadFrom() to load exactly what you want - but, note that assemblies loaded that way can't be executed.)
LoadFile() has a catch. Since it doesn't use a binding context, its dependencies aren't automatically found in its directory. If they aren't available in the Load context, you would have to subscribe to the AssemblyResolve event in order to bind to them.
See here.
Also see Choosing a Binding Context article on the same blog.
回答3
After a lot of head-scratching I've discovered a difference myself this afternoon.
I wanted to load a DLL at runtime, and the DLL lived in another directory. That DLL had its own dependencies (DLLs) which also lived in that same directory.
LoadFile(): Loaded the specific DLL, but not the dependencies. So when the first call was made from within the DLL to one of those other DLLs it threw a FileNotFoundException.
LoadFrom(): Loaded the DLL that I specified and also all the dependencies that lived in that directory.