Dotnet Core Multiple Startup Classes with In-Process Hosting

17.8k Views Asked by At

I have a dotnet core v.2.1 application that utilizes the "startup-class-by-environment-name-convention" to use different Startup classes for different environment, e.g. development, staging and production. The Program.Main.CreateWebHost method looks similar to this:

public static IWebHostBuilder CreateWebHostBuilder(string[] args)
{
    var startupAssembly = Assembly.GetExecutingAssembly();
    var webHostBuilder = WebHost.CreateDefaultBuilder(args)
                                .UseStartup(startupAssembly.FullName);
    return webHostBuilder;
}

However, after upgrading to dotnet core v.2.2 (and the startup-switching still works great) I wanted to try out the in-process hosting capabilities. When switching to the in-process hosting model, and running locally with Visual Studio 2017 updated and IIS Express, I get this error running the application:

HTTP Error 500.30 - ANCM In-Process Start Failure

Common causes of this issue:

  • The application failed to start
  • The application started but then stopped
  • The application started but threw an exception during startup

Troubleshooting steps:

  • Check the system event log for error messages
  • Enable logging the application process' stdout messages
  • Attach a debugger to the application process and inspect

For more information visit: https://go.microsoft.com/fwlink/?LinkID=2028265

I checked all the logs, and all I could find was this:

<Event xmlns="http://schemas.microsoft.com/win/2004/08/events/event">
  <System>
    <Provider Name="IIS Express AspNetCore Module V2" /> 
    <EventID Qualifiers="0">1007</EventID> 
    <Level>2</Level> 
    <Task>0</Task> 
    <Keywords>0x80000000000000</Keywords> 
    <TimeCreated SystemTime="2018-12-14T10:37:48.327935100Z" /> 
    <EventRecordID>3693</EventRecordID> 
    <Channel>Application</Channel> 
    <Computer>[whatever]</Computer> 
    <Security /> 
  </System>
  <EventData>
    <Data>Application '/LM/W3SVC/2/ROOT' with physical root '[whatever again]' failed to load clr and managed application. CLR worker thread exited prematurely</Data> 
    <Data>Process Id: 29836.</Data> 
    <Data>File Version: 12.2.18316.0. Description: IIS ASP.NET Core Module V2 Request Handler. Commit: ce8cf65589734f82b0536c543aba5bd60d0a5a98</Data> 
  </EventData>
</Event>

I read all the dotnet core in-hosting migrate 2.1 -> 2.2 whatever MSDN articles I could find, and tried a bunch of different set-ups, but could find no solution apart from using the default:

public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
    WebHost.CreateDefaultBuilder(args)
           .UseStartup<Startup>();

... Which will not do - I want to use the startup-switching together with the in-process hosting. Does anyone know how to achieve this, or have any suggestions on how to proceed with the troubleshooting?

EDIT: I got the answer I needed from @cilerler. For the sake of completeness, here's what was going on in my case:

The loading of my custom configuration files failed because this process depended on a call to Directory.GetCurrentDirectory(), the result of which changes when switching to in-process hosting. Here's my original code for that part (shortened for brevity):

var basePath = $"{Directory.GetCurrentDirectory()}\\ConfigurationFiles";
builder.SetBasePath(basePath);

builder.AddJsonFile("some.config.json", false, true);
builder.AddJsonFile($"some.config.{context.HostingEnvironment.EnvironmentName}.json", true, true);

The crucial part is - again - the call to GetCurrentDirectory(), so, to fix the issue, I changed the above as per the recommendation in the accepted answer, to:

var currentDirectory = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
var basePath = $"{currentDirectory}\\ConfigurationFiles";
builder.SetBasePath(basePath);

For further details, see the accepted answer ;)

2

There are 2 best solutions below

4
On BEST ANSWER

According to aspnet-core-module article it says

GetCurrentDirectory returns the worker directory of the process started by IIS rather than the app's directory (for example, C:\Windows\System32\inetsrv for w3wp.exe).

which means config loader will not be able to find appsettings.* files, or any other files such as custom config files, that depend on a GetCurrentDirectory call. In order to solve it in your Program.cs right after public static void Main(string[] args) { add the following line

Directory.SetCurrentDirectory(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location));

Also, in project file (e.g. MyProject.csproj) make sure that you have the following lines and appsettings.* exists in output folder.

<ItemGroup>
  <Content Update="appsettings.json">
    <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
  </Content>
  <Content Update="appsettings.Development.json">
    <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
  </Content>
  <Content Update="appsettings.Production.json">
    <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
  </Content>
</ItemGroup>
0
On

I had the same issue, different story.

Visual Studio 2017 - asp.net core mvc (sdk 2.2.104).

  1. Run the application. (yes it will give you the error again)
  2. in VS go to Exception Settings and enable C++ Exceptions, CLR Exceptions, Win32 Exceptions
  3. Restart your debugging progress and see if an exception is thrown.

In my case: I registered a service in Startup.cs via .AddScoped where the concrete serviceType parameter was actually an abstract class (forgot to change this after refactoring).

Else go to Windows Event Viewer and try to find a clue in the logs.