Assembly Loading in .NET Core

2019-03-30 04:42发布

问题:

Using VS2017 RC, .NET Core

I am trying to load an assembly from a file. The dependencies of this assembly are in the same folder.

I am using AssemblyLoadContext.Default.LoadFromAssemblyPath.

I realize LoadFromAssemblyPath exclusively loads the requested assembly, ignoring its dependencies; any attempt to iterate through the assembly types fails with a System.Reflection.ReflectionTypeLoadException.

LoaderExceptions contains a list of System.IO.FileNotFoundException.

I'm curious as to why this is the case, since all the required files are in the same folder.

I also tried to load all *.dll files in a folder, but some surprisingly fail with a System.IO.FileLoadException.

What am I doing wrong?

Edit: I wouldn't want to rely on the .deps file (thus ruling out DependencyContext). Is it possible?

回答1:

Well what works for me is to register a handle with the Resolving event and load required assemblies on demand when LoadFromAssemblyPath needs dependencies. Be aware that this my solution from hours of trial and error, so it might not be the most ideal way. It works for me by now though. Here's my code:

    AssemblyLoadContext.Default.Resolving += (context, name) =>
    {
        // avoid loading *.resources dlls, because of: https://github.com/dotnet/coreclr/issues/8416
        if (name.Name.EndsWith("resources"))
        {
            return null;
        }

        var dependencies = DependencyContext.Default.RuntimeLibraries;
        foreach (var library in dependencies)
        {
            if (IsCandidateLibrary(library, name))
            {
                return context.LoadFromAssemblyName(new AssemblyName(library.Name));
            }
        }

        var foundDlls = Directory.GetFileSystemEntries(new FileInfo(<YOUR_PATH_HERE>).FullName, name.Name + ".dll", SearchOption.AllDirectories);
        if (foundDlls.Any())
        {
            return context.LoadFromAssemblyPath(foundDlls[0]);
        }

        return context.LoadFromAssemblyName(name);
    };
}
private static bool IsCandidateLibrary(RuntimeLibrary library, AssemblyName assemblyName)
{
    return (library.Name == (assemblyName.Name))
            || (library.Dependencies.Any(d => d.Name.StartsWith(assemblyName.Name)));
}

The IsCandidateLibrary() bit originates from there: http://www.michael-whelan.net/replacing-appdomain-in-dotnet-core/

I think you could omit this and the whole DependencyContext part, but it acts as a cache and avoids reloading the same assemblies over and over again. So i kept it.