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.
oAssembly
instance evertwhere to use any type from the loaded assembly?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:
var asm = Assembly.LoadFrom(@"D:\Projects\_Libraries\FluentNH 1.1\Castle.Core.dll");
var obj = asm.CreateInstance("Castle.Core.GraphNode");
var type = Type.GetType(obj.GetType().AssemblyQualifiedName, true); // fails
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 type
will always be null
.
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 (see RelativeSearchPath
). Assembly.Load(string,..)
uses this context.Assembly.LoadFrom
.Assembly.Load(byte\[\],..)
and Assembly.LoadFile
methods, 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 in AppDomain.RelativeSearchPath
and. 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.Load
FileNotFoundException
, which can be rather confusing).You can test this for yourself: Assembly.Load
will not work when you just supply the assembly name to it.
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 method Type.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 set sAssemblyName
, 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 LoadFrom
again. I tested the following and that works splendidly and quite fast:
// works for any loaded assembly, regardless of the path
private static Assembly CurrentDomainOnAssemblyResolve(object sender, ResolveEventArgs args)
{
// you may not want to use First() here, consider FirstOrDefault() as well
var asm = (from a in AppDomain.CurrentDomain.GetAssemblies()
where a.GetName().FullName == args.Name
select a).First();
return asm;
}
// set it as follows somewhere in the beginning of your program:
AppDomain.CurrentDomain.AssemblyResolve += CurrentDomainOnAssemblyResolve;
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.TypeResolve
event 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.
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 an AssemblyResolve
event handler, which isn't that hard to make generically applicable.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With