Dynamically Loading an ASP.NET Core Assembly

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.

asp.net-core
asp.net-core-mvc
.net-core
typeloadexception
asp.net-core-testhost

1 Answer

-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.


User contributions licensed under CC BY-SA 3.0