Cómo cargar ensamblajes ubicados en una carpeta en la aplicación de consola central de .net

Estoy haciendo una aplicación de consola en la plataforma .Net Core y me preguntaba, ¿cómo cargar uno los ensamblajes (archivos .dll) y crear instancias de clases utilizando las características dinámicas de C #? Parece muy diferente a .Net 4.X y no está realmente documentado …

Por ejemplo, digamos que tengo una biblioteca de clases (.Net Core) y solo tiene una clase:

namespace MyClassLib.SampleClasses { public class Sample { public string SayHello(string name) { return $"Hello {name}"; } public DateTime SayDateTime() { return DateTime.Now; } } } 

Entonces, el nombre del archivo dll sería MyClassLib.dll y está ubicado en /dlls/MyClassLib.dll .

Ahora quiero cargar esto en una aplicación de consola simple (.Net Core) y crear una instancia de la clase Sample y llamar a los métodos usando las características dinámicas de C # en la siguiente aplicación de consola:

 namespace AssemblyLoadingDynamic { public class Program { public static void Main(string[] args) { // load the assembly and use the classes } } } 

Nota: Por .Net Core me refiero a la versión RC2.

Actualmente se ejecuta en contra de netcoreapp1.0 , en realidad no necesita llegar al punto de implementar su propio AssemblyLoader . Existe un valor Default que funciona bien. (Por lo tanto, @ VSG24 menciona que Load no hace nada).

 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); } } } 

con project.json ve así:

 { "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" } } } 

No estoy seguro de si es la mejor manera de hacerlo, pero esto es lo que se me ocurrió:

( Solo probado en .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 es el espacio de nombres y Sample es el tipo aka class name.

Cuando se ejecute, intentará cargar la biblioteca de clases comstackda SampleClassLib.dll en la memoria y le dará a mi consola acceso a MyClassLib.SampleClasses.Sample (eche un vistazo a la pregunta) y luego mi aplicación llama al método SayHello() y lo pasa ” John Doe “como su nombre, por lo tanto, el progtwig imprime:

"Hello John Doe"

Nota rápida: la anulación del método Load no importa, por lo que también podría reemplazar su contenido con throw new NotImplementedException() y no debería afectar a nada que nos interese.

Gracias por compartir. Está trabajando con Net Core 1.0 también. Si su ensamblaje necesita otros conjuntos en la misma ruta, puede usar el siguiente ejemplo de código.

 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); } } 

Recuerde agregar las siguientes dependencias a su archivo project.json :

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

Usando .net core 1.1 / estándar 1.6, encontré que AssemblyLoader no estaba disponible, y

  AssemblyLoadContext.Default.LoadFromAssemblyPath (assemblyPath) 

me dio un error “No se pudo cargar el archivo o el ensamblaje xxx”.

Finalmente, esta solución a continuación me funcionó, simplemente agregando un paso para obtener el objeto AssemblyName. Espero que ayude a cualquiera que se quede atascado:

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

@Rob, la única forma en que podría obtener tu ejemplo para construir era cambiar tu tipo “myInstance” a dynamic .

Dejar el tipo como var no permite que se construya el código, pero tan pronto como bash utilizar un método del ensamblado cargado en tiempo de ejecución, obtengo errores de comstackción como myInstance que no contiene el método X. Soy nuevo en esto, pero marcar el tipo como dynamic, parece tener sentido. Si el tipo se carga en tiempo de ejecución, ¿cómo puede el comstackdor verificar que myInstance contendrá el método X o prop Y? Al escribir myInstance como dynamic, creo que está eliminando la verificación del comstackdor y, por lo tanto, podría obtener el ejemplo para comstackr y ejecutar sin problemas. No estoy seguro de que esto sea 100% correcto (no sé lo suficiente y puede avisar que hay un problema al usar la dinámica) pero es la única forma en que lo hago funcionar sin tener que tomarme la molestia de crear el mío AssemblyLoader (como correctamente indica).

Asi que…

 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"); } } } 

Espero que esto ayude a alguien por ser nuevo, me llevó años descubrir por qué mi instancia como var no tenía el método X, etc. Doh!