AssemblyLoadContext in C#
The AssemblyLoadContext class in .NET Core and later is a mechanism for loading and managing assemblies at runtime. It provides a way to control the loading and unloading of assemblies, offering flexibility for scenarios like plugins, dynamic loading, and isolation.
1. What is AssemblyLoadContext?
- It is the .NET Core equivalent of AppDomains in .NET Framework, but it focuses specifically on assembly loading and unloading.
- Each
AssemblyLoadContextacts as an isolated context for managing assemblies and their dependencies.
2. Why Use AssemblyLoadContext?
a. Plugin Development
Load plugins dynamically without impacting the main application or other plugins.
b. Assembly Unloading
Unload assemblies to reclaim memory and resources when they are no longer needed.
c. Dependency Isolation
Load different versions of an assembly or conflicting dependencies in separate contexts.
d. Testing and Scripting
Load assemblies dynamically during testing or executing user-provided scripts.
3. Key Features of AssemblyLoadContext
a. Default vs. Custom Contexts
- Default Context: The default loading context for the application, typically used for loading the application’s main assemblies.
- Custom Context: Allows for creating isolated contexts for loading assemblies independently.
b. Unloading
Custom contexts can unload assemblies using garbage collection when no references remain.
c. Assembly Resolution
Customizable resolution logic for locating and loading assemblies.
4. Creating and Using AssemblyLoadContext
Example 1: Loading an Assembly Dynamically
Here’s a basic example of loading an assembly dynamically using a custom AssemblyLoadContext:
using System;
using System.Reflection;
using System.Runtime.Loader;
class CustomLoadContext : AssemblyLoadContext
{
protected override Assembly Load(AssemblyName assemblyName)
{
// Custom logic to resolve the assembly
Console.WriteLine($"Loading assembly: {assemblyName.FullName}");
return null; // Return null to use the default resolution.
}
}
class Program
{
static void Main()
{
var context = new CustomLoadContext();
// Load an assembly from a file
Assembly assembly = context.LoadFromAssemblyPath(@"C:\path\to\your\assembly.dll");
// Access types and members of the assembly
Type type = assembly.GetType("Namespace.ClassName");
MethodInfo method = type.GetMethod("MethodName");
method.Invoke(null, null);
// Unloading the context
context.Unload();
Console.WriteLine("Assembly unloaded.");
}
}5. Assembly Unloading
Assemblies loaded in a custom AssemblyLoadContext can be unloaded when:
- All references to the
AssemblyLoadContextand its loaded assemblies are released. - The garbage collector collects the
AssemblyLoadContext.
Get Vikas Jindal’s stories in your inbox
Join Medium for free to get updates from this writer.
Example: Unloading an Assembly
using System;
using System.Reflection;
using System.Runtime.Loader;
class Program
{
static void Main()
{
WeakReference contextReference = LoadAndUnloadAssembly();
// Force garbage collection to unload the context
for (int i = 0; i < 10 && contextReference.IsAlive; i++)
{
GC.Collect();
GC.WaitForPendingFinalizers();
}
Console.WriteLine(contextReference.IsAlive ? "Context is still alive" : "Context has been unloaded");
}
static WeakReference LoadAndUnloadAssembly()
{
var context = new AssemblyLoadContext("CustomContext", isCollectible: true);
Assembly assembly = context.LoadFromAssemblyPath(@"C:\path\to\your\assembly.dll");
// Create a WeakReference to the context for monitoring unloading
var weakReference = new WeakReference(context);
// Unload the context
context.Unload();
return weakReference;
}
}6. Assembly Resolution in AssemblyLoadContext
Custom Resolution Logic
You can override the Load method to define custom assembly resolution logic.
Example: Resolving Dependencies
using System;
using System.IO;
using System.Reflection;
using System.Runtime.Loader;
class CustomLoadContext : AssemblyLoadContext
{
private string _dependencyPath;
public CustomLoadContext(string dependencyPath)
{
_dependencyPath = dependencyPath;
}
protected override Assembly Load(AssemblyName assemblyName)
{
string assemblyPath = Path.Combine(_dependencyPath, assemblyName.Name + ".dll");
if (File.Exists(assemblyPath))
{
return LoadFromAssemblyPath(assemblyPath);
}
return null; // Use default resolution if not found
}
}
class Program
{
static void Main()
{
var context = new CustomLoadContext(@"C:\path\to\dependencies");
Assembly assembly = context.LoadFromAssemblyName(new AssemblyName("DependencyName"));
Console.WriteLine($"Loaded assembly: {assembly.FullName}");
}
}7. Best Practices
- Use
isCollectible: truefor Unloading:
- Set
isCollectibletotruewhen creating the context to enable unloading.
2. Monitor Dependencies:
- Ensure that no references to assemblies in the context remain to allow for proper unloading.
3. Handle Exceptions:
- Handle scenarios where assemblies cannot be resolved.
4. Avoid Overloading the Default Context:
- Use custom contexts for loading assemblies dynamically to prevent polluting the default context.
8. Limitations
- Cannot Unload Default Context:
- Only custom contexts support unloading.
2. Requires Careful Dependency Management:
- Circular dependencies or missing references can cause runtime issues.
3. Garbage Collection Dependency:
- Assembly unloading is tied to garbage collection, so it’s not immediate.
9. Practical Scenarios for AssemblyLoadContext
- Plugin Systems:
- Load plugins dynamically and unload them when no longer needed.
2. Runtime Updates:
- Dynamically load updated assemblies without restarting the application.
3. Testing Environments:
- Load test assemblies in isolation to avoid conflicts.
4. Script Execution:
- Execute user-provided scripts with isolated dependencies.




