How to load assemblies located in a folder in .NET Core console app

Solution 1:

Currently running against netcoreapp1.0 you don't actually need to go to the extent of implementing your own AssemblyLoader. There is a Default that exists which works just fine. (Hence @VSG24 mentioning that the Load doesn't do anything).

using System;
using System.Runtime.Loader;

namespace AssemblyLoadingDynamic
{
    public class Program
    {
        public static void Main(string[] args)
        {
            var myAssembly = AssemblyLoadContext.Default.LoadFromAssemblyPath(@"C:\MyDirectory\bin\Custom.Thing.dll");
            var myType = myAssembly.GetType("Custom.Thing.SampleClass");
            var myInstance = Activator.CreateInstance(myType);
        }
    }   
}

with project.json looking like:

{
  "version": "1.0.0-*",
  "buildOptions": {
    "emitEntryPoint": true
  },

  "dependencies": {
    "Microsoft.NETCore.App": {
      "type": "platform",
      "version": "1.0.1"
    },
    "System.Runtime.Loader": "4.0.0"
  },

  "frameworks": {
    "netcoreapp1.0": {
      "imports": "dnxcore50"
    }
  }
}

Solution 2:

Not sure if it's the best way to do it but here's what I came up with:

(Only tested on .Net Core RC2 - Windows)

using System;
using System.Linq;
using System.Reflection;
using System.Runtime.Loader;
using Microsoft.Extensions.DependencyModel;

namespace AssemblyLoadingDynamic
{
    public class Program
    {
        public static void Main(string[] args)
        {
            var asl = new AssemblyLoader();
            var asm = asl.LoadFromAssemblyPath(@"C:\Location\Of\" + "SampleClassLib.dll");

            var type = asm.GetType("MyClassLib.SampleClasses.Sample");
            dynamic obj = Activator.CreateInstance(type);
            Console.WriteLine(obj.SayHello("John Doe"));
        }

        public class AssemblyLoader : AssemblyLoadContext
        {
            // Not exactly sure about this
            protected override Assembly Load(AssemblyName assemblyName)
            {
                var deps = DependencyContext.Default;
                var res = deps.CompileLibraries.Where(d => d.Name.Contains(assemblyName.Name)).ToList();
                var assembly = Assembly.Load(new AssemblyName(res.First().Name));
                return assembly;
            }
        }
    }
}

MyClassLib.SampleClasses is the namespace and Sample is the type aka class name.

When executed, it will try to load the SampleClassLib.dll compiled class library in the memory and gives my console app access to MyClassLib.SampleClasses.Sample (take a look at the question) then my app calls the method SayHello() and passes "John Doe" as name to it, Therefore the program prints:

"Hello John Doe"

Quick note: The override for the method Load doesn't matter so you might as well just replace its content with throw new NotImplementedException() and it shouldn't affect anything we care about.

Solution 3:

Thanks for your sharing. It is working with Net Core 1.0 also. If your assembly needs another assemblies at the same path, you can use the code sample below.

using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.Loader;
using Microsoft.Extensions.DependencyModel;
public class AssemblyLoader : AssemblyLoadContext
{
    private string folderPath;

    public AssemblyLoader(string folderPath)
    {
        this.folderPath = folderPath;
    }

    protected override Assembly Load(AssemblyName assemblyName)
    {
        var deps = DependencyContext.Default;
        var res = deps.CompileLibraries.Where(d => d.Name.Contains(assemblyName.Name)).ToList();
        if (res.Count > 0)
        {
            return Assembly.Load(new AssemblyName(res.First().Name));
        }
        else
        {
            var apiApplicationFileInfo = new FileInfo($"{folderPath}{Path.DirectorySeparatorChar}{assemblyName.Name}.dll");
            if (File.Exists(apiApplicationFileInfo.FullName))
            {
                var asl = new AssemblyLoader(apiApplicationFileInfo.DirectoryName);
                return asl.LoadFromAssemblyPath(apiApplicationFileInfo.FullName);
            }
        }
        return Assembly.Load(assemblyName);
    }
}

Remember to add the following dependencies to your project.json file:

 "System.Runtime.Loader"
 "Microsoft.Extensions.DependencyModel"

Solution 4:

Using .NET Core 1.1 / Standard 1.6, I found that AssemblyLoader was not available, and AssemblyLoadContext.Default.LoadFromAssemblyPath(assemblyPath) gave me a "Could not load file or assembly xxx" error.

Finally, this solution below worked for me - purely by adding a step to get the AssemblyName object:

var assemblyName = AssemblyLoadContext.GetAssemblyName(assemblyPath);
var assembly = Assembly.Load(assemblyName);

Solution 5:

@Rob, the only way I could get your example to build was to change your "myInstance" type to dynamic.

Leaving the type as var does allow the code to build but as soon as I try and use a method from the runtime loaded assembly, I get compiler errors such as myInstance does not contain method X. I'm new at this but marking the type as dynamic, does seem to make sense. If the type is loaded at runtime then how can the compiler verify myInstance will contain method X or prop Y ? By typing the myInstance as dynamic I believe you are removing the compiler checking and thus I could get the example to build and run just fine. Not sure this is 100% the correct way ( I don't know enough and you may advise that there's an issue using dynamic?) but it is the only way I got it to work without having to go to the trouble of creating my own AssemblyLoader (as you correctly point out).

So...

using System;
using System.Runtime.Loader;

namespace TestApp
{
    class Program
    {
        static void Main(string[] args)
        {
            var myAssembly = AssemblyLoadContext.Default.LoadFromAssemblyPath(@"C:\Documents\Visual Studio 2017\Projects\Foo\Foo\bin\Debug\netcoreapp2.0\Foo.dll");
            var myType = myAssembly.GetType("Foo.FooClass");
            dynamic myInstance = Activator.CreateInstance(myType);
            myInstance.UpperName("test");
        }
    }
}

Hope this helps someone as being new it took me ages to pinpoint why myInstance as a var didn't have method X etc Doh!