How to load another assembly within the current app domain?

2

Issue is taken from this github issue, and is related specifically to building a WebHost using a Startup from another assembly.

Calling .Build() on an IWebHostBuilder configured via WebHostBuilderExtensions.UseStartup(this IWebHostBuilder hostBuilder, Type startupType) where the startupType is retrieved from a separate .exe assembly via its string Assembly name fails at dependency injection time.

With the default ASP.NET Core + .NET Framework project, the following is thrown: System.InvalidOperationException: 'Unable to resolve service for type 'Microsoft.Extensions.Configuration.IConfiguration' while attempting to activate 'WebAppWithAfterBuildTarget.Startup'.'

Minimal repro steps:

Create a simple library project named 'MsBuildTask' targeting net462.

Add a reference to Microsoft.Build.[Framework/Utilities.v4.0] assemblies, and a reference to the Microsoft.AspNetCore + .Http.Abstractions nugets.

Create a class like so:

public class RunStartup : Microsoft.Build.Utilities.Task
{
    public string AssemblyToLoad { get; set; }

    public override bool Execute()
    {
        var startupAssembly = Assembly.LoadFrom(Path.Combine(Directory.GetCurrentDirectory(), AssemblyToLoad));
        var builder = WebHost.CreateDefaultBuilder();
        builder.UseStartup(startupAssembly.FullName);
        var host = builder.Build();

        // ... do more stuff
        return true;
    }
}

Build the MsBuildTask project, copy a path to the dll output (used below).

Create an ASP.NET Core 2.0 + .Net Framework 462 project in visual studio.

In the webapp csproj file, add

<UsingTask AssemblyFile="..\MsBuildTask\bin\Debug\net462\MsBuildTask.dll" TaskName="RunStartup" />
<Target Name="RunStartup" AfterTargets="AfterBuild">
  <RunStartup AssemblyToLoad="$(OutputPath)$(AssemblyName).exe" />
</Target>

Build the webapp. (for debugging purposes, in MsBuildTask>Properties>Debug, you can set Launch as executable, Executable as the path to msbuild, and Application arguments as the path to the webapp, then just hit F5 on the MsBuildTask). Expected result The build task would complete successfully, we could do more interesting stuff with the host.

Actual result Observe that during the AfterBuild targets section runs the MsBuildTask.RunStartup.Execute method.

This fails at the .Build() stage as IConfiguration has not been registered.

Further technical details I've tried copying over the Startup to this project (whilst adding the AspNetCore.Mvc nuget as necessary) and running .UseStartup<Startup>() as in a normal ASP.NET Core app and this succeeds. It is when referencing the assembly by name that I hit this error where the dependencies don't seem to be registered.


So a bit more progress with this: In my asp.net core project, I've Exposed a CreateDefaultBuilder() method which calls into the same methods as above, but using the correct type context. I then invoke Build() on the returned object via reflection. This showed some dependency version mis-matches. I worked around these by adding the the following at the start of my Execute() method:

        AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(OnResolve);

with OnResolve defined like so, essentially forcing the dlls to be loaded from the bin directory of my asp.net core project

    private Assembly OnResolve(object sender, ResolveEventArgs args)
    {
        String currentFolder = Path.Combine(Directory.GetCurrentDirectory(), AssemblyToLoad);
        AssemblyName requestedName = new AssemblyName(args.Name);
        var resolvedAssembly = Assembly.LoadFrom(Path.Combine(currentFolder, requestedName.Name + (requestedName.Name == this.AssemblyName ? ".exe" : ".dll")));
        if (resolvedAssembly.GetName().Version < requestedName.Version)

        {
            return null;
        }

        return resolvedAssembly;
    }

However this seems to fail with the following exception for some reason:

System.Reflection.TargetInvocationException
  HResult=0x80131604
  Message=Exception has been thrown by the target of an invocation.
  Source=mscorlib
  StackTrace:
   at System.RuntimeMethodHandle.InvokeMethod(Object target, Object[] arguments, Signature sig, Boolean constructor)
   at System.Reflection.RuntimeMethodInfo.UnsafeInvokeInternal(Object obj, Object[] parameters, Object[] arguments)
   at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
   at System.Reflection.MethodBase.Invoke(Object obj, Object[] parameters)
   at Swashbuckle.AspNetCore.MSBuild.SwaggerGenerator.Execute() in C:\dev\Swashbuckle.AspNetCore\src\Swashbuckle.AspNetCore.MSBuild\SwaggerGenerator.cs:line 42
   at Microsoft.Build.BackEnd.TaskExecutionHost.Microsoft.Build.BackEnd.ITaskExecutionHost.Execute()
   at Microsoft.Build.BackEnd.TaskBuilder.<ExecuteInstantiatedTask>d__26.MoveNext()

Inner Exception 1:
MissingMethodException: Method not found: 'Newtonsoft.Json.JsonSerializerSettings Microsoft.AspNetCore.Mvc.MvcJsonOptions.get_SerializerSettings()'.
.net
asp.net-core
appdomain
asked on Stack Overflow Jun 27, 2018 by ElFik • edited Jun 28, 2018 by ElFik

0 Answers

Nobody has answered this question yet.


User contributions licensed under CC BY-SA 3.0