Dynamically Loading an ASP.NET Core Assembly
Asked Answered
J

1

7

I am trying to dynamically load a .NET Core DLL containing an ASP.NET Core's Startup class. I then want to instantiate the Startup class and hand it over to the TestHost API, so I can start the site in memory. I've written the following code:

var directoryPath = @"C:\Dlls";
var assemblyFilePath = Path.Combine(directoryPath, "Foo.dll");
var assemblyName = AssemblyLoadContext.GetAssemblyName(assemblyFilePath);
var assembly = new AssemblyLoader(directoryPath).LoadFromAssemblyName(assemblyName);
var startupType = assembly.ExportedTypes
    .FirstOrDefault(x => string.Equals(x.Name, "Startup"));
var webHostBuilder = new WebHostBuilder()
    .UseStartup(startupType)
    .UseUrls(new string[] { "http://localhost" });
using (var testServer = new TestServer(webHostBuilder))
{
    var response = testServer.CreateClient().GetAsync("/");
}

public class AssemblyLoader : AssemblyLoadContext
{
    private readonly string directoryPath;

    public AssemblyLoader(string directoryPath) =>
        this.directoryPath = directoryPath;

    protected override Assembly Load(AssemblyName assemblyName)
    {
        var dependencyContext = DependencyContext.Default;
        var compilationLibraries = dependencyContext
            .CompileLibraries
            .Where(x => x.Name.Contains(assemblyName.Name))
            .ToList();
        if (compilationLibraries.Count > 0)
        {
            return Assembly.Load(new AssemblyName(compilationLibraries.First().Name));
        }
        else
        {
            var file = new FileInfo($"{this.directoryPath}{Path.DirectorySeparatorChar}{assemblyName.Name}.dll");
            if (File.Exists(file.FullName))
            {
                var asemblyLoader = new AssemblyLoader(file.DirectoryName);
                return asemblyLoader.LoadFromAssemblyPath(file.FullName);
            }
        }

        return Assembly.Load(assemblyName);
    }
}

However, this throws a TypeLoadException when assembly.ExportedTypes is called. The full stack trace:

System.TypeLoadException occurred HResult=0x80131522
Message=Method 'ConfigureServices' in type 'Foo.Startup' from assembly 'Foo, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' does not have an implementation. Source= StackTrace: at System.Reflection.RuntimeAssembly.GetExportedTypes(RuntimeAssembly assembly, ObjectHandleOnStack retTypes) at System.Reflection.RuntimeAssembly.GetExportedTypes()

I think it's having trouble loading the ASP.NET Core and other third party DLL's like Newtonsoft.Json.

Jephthah answered 14/6, 2017 at 12:59 Comment(4)
Is the consuming app also a .net core application? was the dll built using <CopyLocalLockFileAssemblies>true</..> so all referenced assemblies are put into the same output folder as the dll?Platus
Everything is .NET Core. I'm actually doing a dotnet publish and then the directoryPath variable points at the published output folder, so all DLL's should be available.Jephthah
Interesting. Note that there's also two variants of ConfigureServices: one that returns null and one that returns IServiceProvider. (IStartup would require the one returning IServiceProvider)Platus
Interestingly, I read a blog post about IStartup this morning and happened to just make the switch to it today for the extra type safety, so I am using it. That said, the error is occuring in assembly.ExportedTypes very early on.Jephthah
J
-1

I am not happy with this answer put I'll post it incase it helps someone.

I was doing a dotnet publish and then trying to run my app from the published output. I managed to get it working by publishing a self contained app using the following command instead:

dotnet publish --self-contained --framework netcoreapp1.1 --runtime win7-x64

A self contained app contains all assemblies required to run the app including the .NET Core runtime. I wanted to avoid having to do this as it requires you to know the runtime identifier (RID) and what platform you are running n before hand.

Given the knowledge that my code above is missing .NET Core runtime assemblies, I've tried to load assemblies from the .NET Core runtime folder but this has failed for me so far.

Jephthah answered 7/7, 2017 at 15:29 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.