ASP.NET Core 2.2 has been in place for some time, with a new feature that can use the new AspNetCoreModuleV2 and deploy with InProcess mode on IIS to dramatically improve performance. These days Azure App Service finally completed the deployment of this new version of the module, I configured my blog to the new module, and it exploded in production. Let's see why and how to solve it.
If you don't know what InProcess mode is, simply speaking, the original ASP.NET Core can actually run on IIS, but it was done by an IIS module called AspNetCoreModule, use dotnet.exe to start Kestrel, so the process name was actually dotnet.exe.
The InProcess model has been added to ASP.NET Core 2.2 to run your app in IIS's own w3wp process. It is said to have 400% performance improvement in this MSDN blog post.
To learn more about these two hosting models, you can check https://docs.microsoft.com/en-us/aspnet/core/fundamentals/servers/index?view=aspnetcore-2.2&tabs=windows#in-process-hosting-model
Production Blow Up
Based on the original understanding of ASP.NET Core, I generally do not use IIS to test my App, I only use Kestrel in the development environment, and then use Azure DevOps to deploy the App to Azure App service, As a result of today's upgrade to InProcess mode, the production environment exploded, while the Kestrel of the development machine is good.
Startup failed, my entire blog site cannot be accessed, holy high!
Root Cause Analysis
Fortunately, the Microsoft Cloud "Azure" offers the world's unique Kudu tools that make it easy to see logs, and logs show:
[2018-12-26 12:06:26.5616][RD00155DB8C92A]
[Fatal][Microsoft.AspNetCore.Hosting.Internal.WebHost]
Application startup exception System.IO.FileNotFoundException: Could not find file 'D:\Windows\system32\urlrewrite.xml'.
File name: 'D:\Windows\system32\urlrewrite.xml'
How did this happen? My code access clearly the root directory of the application, why it ran to the system directory? One more look at the startup log:
[2018-12-26 12:06:23.7946][RD00155DB8C92A]
[Info][Moonglade.Web.Program] Moonglade is starting, hail Microsoft!
--------------------------------------------------------
Version: 10.0.6934.1000
Directory: D:\Windows\system32
x64Process: True
OSVersion: Microsoft Windows 10.0.14393
AppDomain: Moonglade.Web
UserName: moonglade
--------------------------------------------------------
The problem code is:
using (var sr = File.OpenText("urlrewrite.xml"))
{
// ...
}
It turns out that when it's running under Kestrel, Environment.CurrentDirectory points to the application root directory, while running in IIS's InProcess mode, it points to the system directory, which eventually causes the app to explode with either indirect or direct use of Environment.CurrentDirectory code.
Fixing the Problem
We can take advantage of the ContentRootPath property provided in the IHostingEnvironment interface to get the absolute path to the current app directory, which behaves consistently in Kestrel and IIS. My problem code is exactly where you can access IHostingEnvironment, so make the following modifications:
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
string baseDir = env.ContentRootPath;
using (var sr = File.OpenText(Path.Combine(baseDir, "urlrewrite.xml")))
{
//...
}
}
For places that don't have direct access to IHostingEnvironment and you don't want to do Dependency Injections, you can code like this:
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
string baseDir = env.ContentRootPath;
//...
AppDomain.CurrentDomain.SetData(Constants.AppBaseDirectory, baseDir);
}
And call GetData() in places where you are using this path.
var configSource = $@"{AppDomain.CurrentDomain.GetData(Constants.AppBaseDirectory)}\mailConfiguration.xml";
I deployed my blog system again, the problem is fixed!
Other Things to Remind
If you are using VS to debug your application under IIS, like this:
This will generate a debug version of web.config under your application root, and will be used in your CI/CD pipeline all the way up to production! This will make your production application run as debug while your Build is set to release mode. Please do exclude this web.config file manually.
Finally, with ASP.NET Core 2.2, we can no longer take for granted that the development of ASP.NET with/out IIS does not matter. Some things in IIS and Kestrel are behavior inconsistently, so it is recommended that if the production environment is using IIS, be sure to test it with the same configuration on your local IIS before deploying to production.
Comments