9

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?

5
  • I'm not used to this kinda operations, but have you tried calling Assembly.LoadFrom()? Commented Dec 1, 2016 at 10:57
  • 2
    @Phate01 there is no Assembly.LoadFrom in .NET Core. Commented Dec 1, 2016 at 10:58
  • I think you should load assemblies from GAC just by specifying the name (either full or short) of the assembly you want to load. But that's just a guess. Commented Dec 1, 2016 at 11:00
  • 1
    Try this one: stackoverflow.com/a/37896162/4499267 Commented Dec 1, 2016 at 11:02
  • Edited. I wouldn't want to use DependencyContext, as it requires a .deps.json file and a specific project configuration. It's too easy to miss, in my opinion. Commented Dec 1, 2016 at 11:04

2 Answers 2

9

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.

Sign up to request clarification or add additional context in comments.

1 Comment

Does this added event have to be removed after use?
6

There is a great enhancement in .Net Core 3.0+, wire AssemblyLoadContext.Default.Resolving event as given below and all dependencies will be resolved and loaded:

 AssemblyLoadContext.Default.Resolving += (context, name) => {
                string assemblyPath = $"{pluginFolder}\\{name.Name}.dll";                
                if (assemblyPath != null)   
                    return context.LoadFromAssemblyPath(assemblyPath);     
                return null;
            };

Remember to define the variabe pluginFolder

Solution2

You can use the AssemblyDependencyResolver class and resolve dependendencies including ones in .deps.json:

  var resolver = new AssemblyDependencyResolver(pluginPath);            

  AssemblyLoadContext.Default.Resolving += (context, name) => {

                string assemblyPath = resolver.ResolveAssemblyToPath(name);               
                if (assemblyPath != null)   
                    return context.LoadFromAssemblyPath(assemblyPath);     
                return null;
            };

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.