Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Load NuGet dependencies at runtime

Tags:

I'm looking for a way to run code by executing the following steps:

  1. Receiving a list of NuGet packages (a list of tuples ("package name", "package version", "path to main class").
  2. Retrieving them in a local directory (cf code sample #1)
  3. Loading them in my program at run-time
  4. Running the main classes by introspection (cf code sample #2)

By now I am struggling with the third step. I can't find out how to load my package at run-time.

My main question are:

  • How can I find out in which folders were stored the retrieved packages?
  • How can I load the content of those directories into my program?

Code Sample #1:

private static void getPackageByNameAndVersion(string packageID, string version) {     IPackageRepository repo =             PackageRepositoryFactory.Default                   .CreateRepository("https://packages.nuget.org/api/v2");     string path = "C:/tmp_repo";    PackageManager packageManager = new PackageManager(repo, path);    Console.WriteLine("before dl pkg");    packageManager.InstallPackage(packageID, SemanticVersion.Parse(version));  } 

Code sample #2:

private static void loadByAssemblyNameAndTypeName(string assemblyName, string typeName) {    AppDomain isolationAppDomain = AppDomain.CreateDomain("tmp");    object a = isolationAppDomain.CreateInstanceAndUnwrap(assemblyName, typeName);    Type x = a.GetType();    MethodInfo m = x.GetMethod("Main");    m.Invoke(a, new object[] { }); } 
like image 210
Manuel Leduc Avatar asked Aug 06 '15 15:08

Manuel Leduc


1 Answers

Grab a cup of coffee :)

Downloading the nuget package?

Nuget.Core (nuget package) is a good choice, and here is a snippet of code that I have that should be able to download a nuget package by id and version

var repo = PackageRepositoryFactory.Default                 .CreateRepository("https://packages.nuget.org/api/v2");  string path = "c:\\temp"; var packageManager = new PackageManager(repo, path); packageManager.PackageInstalled += PackageManager_PackageInstalled;  var package = repo.FindPackage("packageName", SemanticVersion.Parse("1.0.0")); if (package != null) {     packageManager.InstallPackage(package, false, true); } 

Notice that I plugged an event handler to the PackageInstalled event of the PackageManager class.

How do we load an assembly in an isolated app domain?

Since reflection API does not provide a way to load an assembly in a specific domain, We will create a proxy class that act as a loader in our isolated domain:

public class TypeProxy : MarshalByRefObject {     public Type LoadFromAssembly(string assemblyPath, string typeName)     {         try         {             var asm = Assembly.LoadFile(assemblyPath);             return asm.GetType(typeName);         }         catch (Exception) { return null; }     } } 

And now, is how to put it all together?

Here comes the complex part:

private static void PackageManager_PackageInstalled(object sender,                                                      PackageOperationEventArgs e) {     var files = e.FileSystem.GetFiles(e.InstallPath, "*.dll", true);     foreach (var file in files)     {         try         {             AppDomain domain = AppDomain.CreateDomain("tmp");             Type typeProxyType = typeof(TypeProxy);             var typeProxyInstance = (TypeProxy)domain.CreateInstanceAndUnwrap(                     typeProxyType.Assembly.FullName,                     typeProxyType.FullName);              var type = typeProxyInstance.LoadFromAssembly(file, "<KnownTypeName>");             object instance =                  domain.CreateInstanceAndUnwrap(type.Assembly.FullName, type.FullName);         }         catch (Exception ex)         {             Console.WriteLine("failed to load {0}", file);             Console.WriteLine(ex.ToString());         }      } } 

Notice that this method is the event handler that gets executed after downloading the nuget package

Also

Note that you will need to replace <KnownTypeName> with the expected type name coming from the assembly (or maybe run a discovery of all public types in the assembly)


Worth noting that I haven't executed this code myself and cannot guarantee that it will work out of the box, and still might need some tweaking. but Hopefully it is the concept that allows you to solve the problem.

like image 163
Bishoy Avatar answered Sep 19 '22 22:09

Bishoy