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()'.
User contributions licensed under CC BY-SA 3.0