Search code examples
c#.net.net-coredynamic-loading.net-core-3.0

Loading and then Unloading DLL with dependency locks the DLL


Loading DLL with dependency dynamically and then unloading it, still lock the DLL and I am unable to delete/replace the dll.

As part of writing a plugin app i'm dynamically load DLL (which has dependency e.g Newtonsoft.Json), run the loaded assembly and then unload it. after unloading,I can't delete the DLL from disk (until I restart my app), BUT, if I use DLL which doesn't have dependencies, it works fine, and not lock the file. The implementation is based on .NET core 3 load/unload taken from: https://learn.microsoft.com/en-us/dotnet/standard/assembly/unloadability

I use the AssemblyLoadContext which has a resolver, e.g:

class TestAssemblyLoadContext : AssemblyLoadContext
{
    private AssemblyDependencyResolver _resolver;

    public TestAssemblyLoadContext(string mainAssemblyToLoadPath) : base(isCollectible: true)
    {
        _resolver = new AssemblyDependencyResolver(mainAssemblyToLoadPath);
    }

    protected override Assembly Load(AssemblyName name)
    {
        string assemblyPath = _resolver.ResolveAssemblyToPath(name);
        if (assemblyPath != null)
        {
            return LoadFromAssemblyPath(assemblyPath);
        }

        return null;
    }
}

and the code that create the context:

    [MethodImpl(MethodImplOptions.NoInlining)]
    public static void runCommands(string pluginPath, bool execute,out WeakReference alcWeakRef)
    {
        string pluginLocation = getPath(pluginPath);

        PluginLoadContext loadContext = new PluginLoadContext(pluginLocation);
        alcWeakRef = new WeakReference(loadContext, trackResurrection: true);

        Assembly pluginAssembly = loadContext.LoadFromAssemblyName(new AssemblyName(Path.GetFileNameWithoutExtension(pluginLocation)));

        var commands = CreateCommands(pluginAssembly).ToList();


        if (execute) {
            Console.WriteLine("Commands: ");
            foreach (ICommand command in commands)
            {
                Console.WriteLine($"executing... {command.Execute()}");
            }
        }

        commands.Clear();
        loadContext.Unload();
    }

I wander if this is something I'm doing wrong, I already tried loading the file from stream e.g:

using (var fs = new FileStream(pluginLocation, FileMode.Open, FileAccess.Read))
{
    var pluginAssembly = loadContext.LoadFromStream(fs);
    ....
    ....
}

Solution

  • The problem solved, Basically when unloading DLL, you can't do it if you have a Newtonsoft.Json dependency, because they have a bug which locks the file.

    This is based on response from github issue I opened