visual una tipos standard proyecto net librerias libreria introduccion framework crear como clases biblioteca c# .net .net-core .net-core-rc2

una - tipos de librerias en c#



Cómo cargar ensamblados ubicados en una carpeta en la aplicación de consola.net core (6)

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

Por ejemplo, supongamos 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) e instanciar la clase Sample y llamar a los métodos usando 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.


@Rob, la única forma en que pude obtener su ejemplo para construir fue cambiar su tipo "myInstance" a dinámico .

Dejar el tipo como var permite que el código se construya, pero tan pronto como intento usar un método del ensamblado cargado en tiempo de ejecución, obtengo errores de compilación como myInstance no contiene el método X. Soy nuevo en esto, pero marcar el tipo como dinámico parece tener sentido. Si el tipo se carga en tiempo de ejecución, ¿cómo puede verificar el compilador que myInstance contendrá el método X o el accesorio Y? Al escribir myInstance como dinámico, creo que está eliminando la comprobación del compilador y, por lo tanto, podría obtener el ejemplo para compilar y ejecutar perfectamente. No estoy seguro de que sea el 100% de la manera correcta (no sé lo suficiente y ¿puede aconsejarme que haya un problema con el uso dinámico?) AssemblyLoader (como señala correctamente).

Entonces...

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 como nuevo, me llevó mucho tiempo determinar por qué myInstance como var no tenía el método X, etc. ¡Doh!


Actualmente se ejecuta contra netcoreapp1.0 , en realidad no es necesario que implemente 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 como:

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


Excavo mucho en eso, probé el enfoque de DependencyContext ... Funciona bien pero tiene algunas limitaciones y es diferente de la resolución de ensamblaje estándar que se encuentra en la aplicación c ++ dotnet que inicia su aplicación. Debe hacer coincidir los nombres manualmente y si su aplicación host es publicada, no tendrá la ruta de prueba para la carpeta nuget, lo cual es un problema (solucionable) si su ensamblaje secundario está en depuración y usa nuget ...

Entonces, aquí hay otra solución: si la aplicación (ensamblaje A) que carga manualmente un ensamblaje (ensamblaje B) no tiene dependencias (o no tiene dependencias conflictivas con el ensamblaje B), sugiero hacer trampa y omitir la resolución del ensamblaje B. Hay una gema oculta para dotnet.exe que le permite cargar el archivo deps de su elección para que pueda hacer algo como esto:

dotnet exec --depsfile pathToAssemblyB/assemblyB.deps.json --runtimeconfig pathToAssemblyB/assemblyB.runtimeconfig.json AssemblyA.dll

y luego puede cargar el ensamblaje como se explica en otras respuestas con

var myAssembly = AssemblyLoadContext.Default.LoadFromAssemblyPath("pathToAssemblyB//AssemblyB.dll");

De esta manera, resolverá correctamente todas las dependencias para el ensamblado B pero no para el ensamblado A. Es un problema inverso, pero si tiene una aplicación pequeña que quiere hacer algo de comunicación remota en una aplicación grande, es útil. Otro problema es que necesita saber que va a usar assembleB al iniciar su aplicación y que solo funciona una vez por ejecución. Por lo tanto, hay un conjunto diferente de problemas y puede elegir su enfoque según su situación. Tenga en cuenta que es una característica no admitida / no documentada, pero se utiliza en las herramientas principales de EF, por lo que es "viable" por ahora ...


Gracias por compartir. También está trabajando con Net Core 1.0. Si su ensamblaje necesita otros ensamblajes en la misma ruta, puede usar el ejemplo de código a continuación.

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"


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 conocido como nombre de clase.

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

"Hello John Doe"

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


Usando .net core 1.1 / standard 1.6, descubrí que AssemblyLoader no estaba disponible, y

AssemblyLoadContext.Default.LoadFromAssemblyPath(assemblyPath) me dio el error "No se pudo cargar el archivo o el ensamblaje xxx".

Finalmente, esta solución a continuación funcionó para mí, 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);