Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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
        Try
            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.

like image 423
Josh Smeaton Avatar asked May 24 '10 07:05

Josh Smeaton


People also ask

How assembly load works?

Loads an assembly given its AssemblyName. The assembly is loaded into the domain of the caller using the supplied evidence. Loads the assembly with a common object file format (COFF)-based image containing an emitted assembly. The assembly is loaded into the application domain of the caller.

What is a dynamic assembly C#?

Dynamic assemblies are those assemblies which are not stored on the disk before execution in fact after execution they get stored on the disk. When .NET runtime calls them they are directly loaded from the memory not from the disk.

What is DLL in assembly?

An assembly is a collection of one or more files and one of them DLL or EXE. DLL contains library code to be used by any program running on Windows. A DLL may contain either structured or object oriented libraries. A DLL file can have a nearly infinite possible entry points.

What is the role of assemblies in CLR?

Assemblies in the common language runtime Assemblies provide the common language runtime with the information it needs to be aware of type implementations. To the runtime, a type doesn't exist outside the context of an assembly.


2 Answers

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.

like image 79
Hans Passant Avatar answered Oct 17 '22 16:10

Hans Passant


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();
like image 21
Daniel Wolf Avatar answered Oct 17 '22 17:10

Daniel Wolf