Problem with dynamically loading assemblies in .NET
We've built a small component which takes an Id, looks up an entry in the database for an assembly/namespace/class, and dynamically loads an instance of the class that we're after. It has been working fine up until now, but when running this code in VS 2010, it's failing.
Private Function AssemblyLoaded(ByVal assemblyFile As String) As Assembly
Dim assemblies() As Assembly = AppDomain.CurrentDomain.GetAssemblies
For Each asmb As Assembly In assemblies
If (asmb.Location = assemblyFile)) Then Return asmb
Next
Return Nothing
End Function
The first problem is, when the iterator hits a Dynamic Assembly, there is no asmb.Location, and a NotSupportedException is thrown. Is there any way to check for the Unsupported-ness of the Location field without having to catch the exception?
The second problem, asmb.Location is returning the whole path instead of just the filename, which means this function fails every single time. If this function determines that a class isn't already loaded, then we try to load it and get an AccessViolationException because the class has already loaded and we can't "re-load" it.
Changing the function to this works:
Private Function AssemblyLoaded(ByVal assemblyFile As String) As Assembly
Dim assemblies() As Assembly = AppDomain.CurrentDomain.GetAssemblies
For Each asmb As Assembly In assemblies
T开发者_运维百科ry
If (asmb.Location.EndsWith(assemblyFile)) Then Return asmb
Catch ex As NotSupportedException
Continue For
End Try
Next
Return Nothing
End Function
But it feels dirty. Is there a better way of checking if an assembly is already loaded, and handing that back to the caller? Are the issues above specific to .NET 4.0 or Visual Studio 2010? I haven't tried this outside the IDE as it requires fairly significant configuration.
You can check if the assembly is dynamic by skipping instances of AssemblyBuilder. You should use Path.GetFileName() to isolate the name. Beware that this is not a very good idea since assemblies from different paths may have identical names. But you seem to be stuck with this. Thus:
Private Function AssemblyLoaded(ByVal assemblyFile As String) As Assembly
For Each asmb As Assembly In AppDomain.CurrentDomain.GetAssemblies
If TypeOf asmb Is System.Reflection.Emit.AssemblyBuilder Then Continue For
If System.IO.Path.GetFileName(asmb.Location) = assemblyFile Then Return asmb
Next
Return Nothing
End Function
Case sensitivity is something you should deal with as well, perhaps.
To add to Hans Passant's anwer: for my application (C# 4.0), skipping instances of AssemblyBuilder
still failed. Turns out there is another class, System.Reflection.Emit.InternalAssemblyBuilder
, that also throws a NotSupportedException
when accessing Location
.
InternalAssemblyBuilder
and RuntimeAssembly
(the desired type) are both internal, so the best I could come up with is (in C#):
var assemblies = AppDomain.CurrentDomain
.GetAssemblies()
.Where(assembly => assembly.GetType().Name == "RuntimeAssembly")
.Select(assembly => assembly.Location)
.ToArray();
If you are using .Net 4.0, you should check Assembly.IsDynamic, which covers all bases...
精彩评论