Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Assembly Loading in .NET Core

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?

like image 929
Raine Avatar asked Dec 01 '16 10:12

Raine


People also ask

What does assembly load do?

Loads an assembly with the specified name. Loads an assembly given its AssemblyName. Loads the assembly with a common object file format (COFF)-based image containing an emitted assembly, optionally including symbols for the assembly. The assembly is loaded into the application domain of the caller.

What is assembly in .NET core?

An assembly is a collection of types and resources that are built to work together and form a logical unit of functionality. Assemblies take the form of executable (.exe) or dynamic link library (. dll) files, and are the building blocks of . NET applications.

How NET finds the assemblies during program execution?

After the correct assembly version has been determined by using the information in the calling assembly's reference and in the configuration files, and after it has checked in the global assembly cache (only for strong-named assemblies), the common language runtime attempts to find the assembly.

What is the method used to load assembly by specifying filename?

LoadFrom(String) Loads an assembly given its file name or path.


2 Answers

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.

like image 152
chrishuen Avatar answered Nov 14 '22 21:11

chrishuen


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;
            };
like image 39
M.Hassan Avatar answered Nov 14 '22 20:11

M.Hassan