Dynamically load assemblies in ASP.NET 5

2019-03-20 18:06发布

问题:

I used to have some code which scanned the bin directory of my application for assemblies which weren't loaded in the AppDomain yet and loaded them. It basically looked like:

foreach (var assemblyPath in Directory.GetFiles("path\to\bin", "*.dll"))
{
    var inspected = Assembly.ReflectionOnlyLoadFrom(assemblyPath);
    Assembly.Load(inspected.GetName());
}

I skipped the try/catch clauses, etc for brevity.

This allowed me to drop assemblies in the bin folder at run-time with implementations for certain interfaces and let the IoC container pick them up automatically. Now with the new Roslyn magic, there are no physical DLL's anymore when debugging. Is there any way to retrieve assembly names, project names or dependency names (in project.json) dynamically.

I guess I have to implement something like this example in the Entropy repo, but I don't know how to implement it for my scenario.

回答1:

What you are looking for is ILibraryManager implementation which provides access to the complete graph of dependencies for the application. This is already flowed through the ASP.NET 5 DI system. So, you can reach out to it from there.

Sample usage can be found inside RoslynCompilationService.



回答2:

You can use the IAssemblyLoadContextAccessor interface to load ASP.NET 5 class library (.xproj) projects dynamically. The following example code works with Beta 4:

public class Startup
{
    public void Configure(IApplicationBuilder app)
    {
        var assemblyLoadContextAccessor = app.ApplicationServices.GetService<IAssemblyLoadContextAccessor>();
        var loadContext = assemblyLoadContextAccessor.Default;
        var loadedAssembly = loadContext.Load("NameOfYourLibrary");
    }
}


回答3:

I solved this issue partly using the ILibraryManager as suggested by @tugberk. I changed the approach a bit which dropped the need of scanning the bin folder for new assemblies. I just want all the loaded assemblies in the current AppDomain.

I injected an instance of the ILibraryManager interface in my type finder class and used the GetReferencingLibraries() method with the name of the core assembly, which is referenced by all the other assemblies in the application.

A sample implementation can be found here, where this is the important part:

public IEnumerable<Assembly> GetLoadedAssemblies()
{
    return _libraryManager.GetReferencingLibraries(_coreAssemblyName.Name)
                          .SelectMany(info => info.Assemblies)
                          .Select(info => Assembly.Load(new AssemblyName(info.Name)));
}


回答4:

For .net core users, here is my code for loading assemblies from a specific path. I had to use directives, as it's slightly different for .Net Framework and .Net Core.

In your class header you'll need to declare the using something similar to:

#if NET46
#else
    using System.Runtime.Loader;
#endif

And in your function something similar to the following:

string assemblyPath = "c:\temp\assmebly.dll";

#if NET46
                Assembly assembly = Assembly.LoadFrom(assemblyPath);
#else
                AssemblyLoadContext context = AssemblyLoadContext.Default;
                Assembly assembly = context.LoadFromAssemblyPath(assemblyPath);
#endif