Implementation of. NET Core 3.0 Recyclable Assembly Load Context

  • 2021-11-02 00:39:31
  • OfStack

1. Past lives

Since the birth of. NET, the dynamic loading and unloading of assemblies is an Hack technology. The previous NetFx used AppDomain to load assemblies. However, AppDomain did not provide API to unload an assembly directly, but unloaded the whole AppDomain to unload all assemblies contained in it. However, uninstalling the whole CurrentAppDomain will make the program not work. Some people may create another AppDomain to load/unload assemblies, but because assemblies cannot be accessed across domains, they can only be accessed through Remote Proxy, which brings a certain difficulty in type creation and use, and it also makes type inheritance quite complicated.

. NET Core 1 is not supported by AppDomain. But in. NET Core 3.0, the one feature I'm looking forward to most is support for collectible assemblies (Collectible AssemblyLoadContext). As we all know, 1 in NET Core directly uses API of AssemblyLoadContext for dynamic loading of assemblies, but does not provide Unload method. This upgrade updates this capability.

2. AssemblyLoadContext

In fact, the design of AssemblyLoadContext this time is more like a replica of ClassLoader in Java, which can be said to be very similar. Custom AssemblyLoadContext during use can manage its assemblies internally and perform Unload on the overall Context. Conflicts between assembly names and versions can also be avoided by using AssemblyLoadContext.

3. Getting Started

. NET Core 3.0 is not officially available, so all the following examples will be completed using a preview version of SDK. I used. NET Core SDK 3.0. 100-preview-009812


dotnet new globaljson --sdk-version 3.0.100-preview-009812

AssemblyLoadContext is an abstract class, so we need to subclass it. The following shows how we created a custom AssemblyLoadContext. To implement a recyclable Context, we need to specify isCollectible: true in the constructor:


public class CollectibleAssemblyLoadContext : AssemblyLoadContext
{
  public CollectibleAssemblyLoadContext() : base(isCollectible: true)
  { }
 
  protected override Assembly Load(AssemblyName assemblyName)
  {
    return null;
  }
}

Create an library using netstandard 2.0


using System;
 
namespace SampleLibrary
{
  public class SayHello
  {
    public void Hello(int iteration)
    {
      Console.WriteLine($"Hello {iteration}!");
    }
  }
}

Testing Load/Unload


var context = new CollectibleAssemblyLoadContext();
var assemblyPath = Path.Combine(Directory.GetCurrentDirectory(),"SampleLibrary.dll");
using (var fs = new FileStream(assemblyPath, FileMode.Open, FileAccess.Read))
{
  var assembly = context.LoadFromStream(fs);

  var type = assembly.GetType("SampleLibrary.SayHello");
  var greetMethod = type.GetMethod("Hello");

  var instance = Activator.CreateInstance(type);
  greetMethod.Invoke(instance, new object[] { i });
}

context.Unload();

GC.Collect();
GC.WaitForPendingFinalizers();

When GC reclaiming is performed, the loaded assembly is completely reclaimed.

4. Finally

GitHub: https://github.com/maxzhang1985/YOYOFx If you feel that you can still ask Star, you are welcome to have an exchange.


Related articles: