[C#] Dynamic Assembly Loading

Thanks to Reflection, in C# it's possible to dynamically load an assembly and invoke its methods. There are two ways to do this: simple loading and contextualized loading (only available in .NET Core). Let's take a closer look.

First, we create a new Library Project that contains only a class with a static method:

namespace HelloAssembly;

public class HelloWorld {
    public static void SayHello() {
        Console.WriteLine("Hello world, it'm a dinamically loaded assembly!");
    }

}

After building, we'll find the compiled dll file in the project folder. The path might look like this 'bin\Release\net8.0\HelloAssembly.dll', or this 'bin\Debug\net8.0\HelloAssembly.dll' (if we compiled the project in debug mode).

With an assembly ready to be loaded, we then create a new Console Project in Visual Studio and attempt a simple load, invoking the SayHello method.

using System.Reflection;

// Load an assembly without a context
Assembly testAssembly = Assembly.LoadFile(@"D:\TempStorage\DynamicAssemblyLoading\HelloAssembly\bin\Release\net8.0\HelloAssembly.dll");
Type? helloWorldType = testAssembly.GetType("HelloAssembly.HelloWorld");
MethodInfo? sayHelloMethod = helloWorldType?.GetMethod("SayHello");
sayHelloMethod?.Invoke(null, null);

This solution is suitable for simpler scenarios, but can cause several issues in loading native libraries or different versions of a dependency. To resolve these issues, .NET Core introduced the AssemblyLoadContext for loading the assembly in a specific context. Let's modify the code to load the assembly in a specific context.

using System.Reflection;
using System.Runtime.Loader;

// Load an assembly with a context
var loadContext = new AssemblyLoadContext("TestAssemblyLoadContext", true);

Assembly testAssemblyWithContext = loadContext.LoadFromAssemblyPath(@"D:\TempStorage\DynamicAssemblyLoading\HelloAssembly\bin\Release\net8.0\HelloAssembly.dll");
Type? helloWorldTypeWithContext = testAssemblyWithContext.GetType("TestAssembly.HelloWorld");
MethodInfo? sayHelloMethodWithContext = helloWorldTypeWithContext?.GetMethod("SayHello");
sayHelloMethodWithContext?.Invoke(null, null);

loadContext.Unload();

AssemblyLoadContext in .NET Core represents a significant evolution in the way assemblies are loaded and managed. This tool becomes essential in various situations:

  1. Assembly Isolation: When you need to isolate assemblies to prevent conflicts, such as in the case of plugins or modules that need to operate independently from the main application. Without a dedicated context, assemblies would share the same namespace and could interfere with each other, especially if they have dependencies with different versions.
  2. Loading Multiple Versions of the Same Assembly: In scenarios where different versions of the same assembly need to be loaded, AssemblyLoadContext allows doing so without conflicts. Without this capability, the default application domain could only load one version of the assembly, limiting flexibility and potentially causing errors if different parts of the application require different versions.
  3. Assembly Lifecycle Management: AssemblyLoadContext provides the ability to unload loaded assemblies. This is particularly useful in long-running applications that need to dynamically load and unload features, thereby avoiding excessive memory consumption and potential memory leaks.
  4. Specific Dependency Management: In complex applications with many dependencies, using separate contexts allows for managing and resolving dependencies for each assembly in isolation, reducing the risk of conflicts and compatibility issues.

Without a context, dynamically loading an assembly can lead to version conflicts, namespace collisions, and performance issues.


You'll only receive email when they publish something new.

More from GSLF
All posts