I am loading an Assembly using Assembly.LoadFrom() as the assemblies are located in a different path from Application Base directory.
Dim oAssembly As Assembly = _
Assembly.LoadFrom("C:\\MyFolder\\" + ddlXlate.SelectedItem.ToString() + ".dll")
And I consume a Type from that assembly without any problem:
oXML = CType(oAssembly.CreateInstance(sBaseType + ".XlateContainer"), _
XlateBase.XlateContainer)
However, the problem occurs when I try to use a Type from this assembly from within another method like the one below:
oComboBox.DataSource = _
[Enum].GetValues(Type.GetType(sType + "+ItemEnum," + sAssemblyName))
sAssemblyName is the one I loaded using LoadFrom() actually. After it said it cannot find the assembly, I used AssemblyResolve event which solved my problem :
Subscribing AssemblyResolve event :
AddHandler AppDomain.CurrentDomain.AssemblyResolve, _
AddressOf MyResolveEventHandler
Event Handler Method:
Private Shared Function MyResolveEventHandler(ByVal sender As Object, _
ByVal args As ResolveEventArgs) As Assembly
Return Assembly.LoadFrom("C:\\PSIOBJ\\" + args.Name + ".dll")
End Function
And I thought maybe the error occurs because it cannot find a dependent assembly defined in assembly manifest file I loaded using LoadFrom() already but when I checked the args.Name, I saw it was trying to load same assembly and after that it worked without any problem. So basically a type in the loaded assembly cannot be found before the event adding change.
My old code was using AppDomain.CurrentDomain.Load() and Assembly.Load() methods and they were working fine without the AssemblyResolve event. I was able to reach types in dynamically loaded Assembly from every where within the same AppDomain.
LoadFrom() can find dependencies automatically within the same requested assembly path and that couldn’t be problem as everything this dll needs was there. So at first it looked like a AppDomain problem to me as it looks like it seems it can reach assemblies from Load context instead of LoadFrom context and I am now using LoadFrom context.
- But now it seems I should pass
oAssemblyinstance evertwhere to use any type from the loaded assembly? - Doesn’t it load the assembly where I can reach it everywhere (same AppDomain) using simple
Type.GetType(...)method?
Can some one please fill the missed points and answer my questions?
You can use C#, in fact I don’t like VB.NET but I have to use it here in Office.
If I understand your question correctly, you are trying to do something along those lines:
The problem that you encounter is, that whatever form of assembly loading you use, when the library is not in the same path as your executable, the variable
typewill always benull.Cause
What you encounter is the problem of different loading contexts for assemblies in .NET. There are generally three, actually four types of loading contexts, in short:
BaseDirectory) and those in the PrivatePath (seeRelativeSearchPath).Assembly.Load(string,..)uses this context.Assembly.LoadFrom.Assembly.Load(byte\[\],..)andAssembly.LoadFilemethods, or when you load a dynamic assembly not saved to disk, this context is used.Types loaded in one context, are not compatible with another context (you cannot even cast equal types from one context to another!). Methods specifically operating on one context, cannot access another context.
Type.GetType(string)can only load types in the default context, unless you help the method a little.This is exactly what you encountered. When the assembly dll was in the path of your application, everything worked fine. As soon as you moved it, things started to fall apart.
More specifically:
When you call
Type.GetType(string), it will query all statically referenced assemblies in the path and dynamically loaded assemblies in the current path (AppDomain.BaseDirectory), the GAC and inAppDomain.RelativeSearchPathand. Unfortunately, the relative search path must be relative to the base directory.Result:
The result of this behavior is that GetType does not simply check all loaded assemblies. Instead, it works the other way around, it does:
Assembly.LoadFileNotFoundException, which can be rather confusing).You can test this for yourself:
Assembly.Loadwill not work when you just supply the assembly name to it.Solutions
There are several solutions. One you already named yourself and that’s keeping the assembly object around. There are some more, each with their own drawbacks:
Use
GetType()on the instantiated object itself, instead of the static methodType.GetType(string). This has the advantage that you don’t need the assembly qualified name of the type, which can be hard to get (in your example, you don’t say how you setsAssemblyName, but isn’t that also something you need floating around?).Use a generic resolver that checks the loaded assemblies and returns the loaded assembly. You don’t need to call
LoadFromagain. I tested the following and that works splendidly and quite fast:Use the AppDomain.CurrentDomain.AssemblyLoad and .AssemblyResolve events together. The first you use to memorize each loaded assembly in a dictionary cache (by full name), the second you use to probe that dictionary by getting the value from it by name. This is relatively trivial to implement and might perform slightly better than the previous solution.
Use the: this doesn’t work. GetType first tries to load the assembly, when that fails, it doesn’t try to resolve the type and this event never fires.AppDomain.CurrentDomain.TypeResolveevent handler. I haven’t tried this, so I’m not certain it’ll work in your scenario.Add the libraries you want to resolve to the GAC or to any (relative) path of your application. This is by far the easiest solution.
Add the paths to app.config. This only works for strongly typed assemblies, in which case you could just as easily load them in the GAC. Not-strongly-typed assemblies must still be in a relative path to the current application.
Conclusion
The static method group
Type.GetType(..)behaves rather unintuitively when it comes to loaded assemblies at first look. Once you understand the ideas behind the several contexts, try to place the assembly in the default context. When that is not possible, you can create anAssemblyResolveevent handler, which isn’t that hard to make generically applicable.