How should a single-file .Net Core 3.0 Web API application be configured to look for the appsettings.json
file that is in the same directory that the single-file application is built to?
After running
dotnet publish -r win-x64 -c Release /p:PublishSingleFile=true
The directory looks like this:
XX/XX/XXXX XX:XX PM <DIR> .
XX/XX/XXXX XX:XX PM <DIR> ..
XX/XX/XXXX XX:XX PM 134 appsettings.json
XX/XX/XXXX XX:XX PM 92,899,983 APPNAME.exe
XX/XX/XXXX XX:XX PM 541 web.config
3 File(s) 92,900,658 bytes
However, attempting to run APPNAME.exe
results in the following error
An exception occurred, System.IO.FileNotFoundException: The configuration file 'appsettings.json' was not found and is not optional. The physical path is 'C:\Users\USERNAME\AppData\Local\Temp\.net\APPNAME\kyl3yc02.5zs\appsettings.json'.
at Microsoft.Extensions.Configuration.FileConfigurationProvider.HandleException(ExceptionDispatchInfo info)
at Microsoft.Extensions.Configuration.FileConfigurationProvider.Load(Boolean reload)
at Microsoft.Extensions.Configuration.FileConfigurationProvider.Load()
at Microsoft.Extensions.Configuration.ConfigurationRoot..ctor(IList`1 providers)
at Microsoft.Extensions.Configuration.ConfigurationBuilder.Build()
at Microsoft.AspNetCore.Hosting.WebHostBuilder.BuildCommonServices(AggregateException& hostingStartupErrors)
at Microsoft.AspNetCore.Hosting.WebHostBuilder.Build()
...
I tried solutions from a similar, but distinct question, as well as other Stack Overflow questions.
I attempted to pass the following to SetBasePath()
Directory.GetCurrentDirectory()
environment.ContentRootPath
Path.GetDirectoryName(Assembly.GetEntryAssembly().Location)
Each led to the same error.
The root of the issue is that the PublishSingleFile
binary is unzipped and run from a temp
directory.
In the case of this single file app, the location it was looking appsettings.json
was the following directory:
C:\Users\USERNAME\AppData\Local\Temp\.net\APPNAME\kyl3yc02.5zs
All of the above methods point to the place that the file is unzipped to, which is different than the place it was run from.
My application is on .NET Core 3.1, is published as a single file and runs as a Windows Service (which may or may not have an impact on the issue).
The proposed solution with
Process.GetCurrentProcess().MainModule.FileName
as the content root works for me, but only if I set the content root in the right place:This works:
This does not work:
If you're okay with having files used at runtime outside of the executable, then you could just flag the files you want outside, in csproj. This method allows for live changes and such in a known location.
If this is not acceptable, and must have ONLY a single file, I pass the single-file-extracted path as the root path in my host setup. This allows configuration, and razor (which I add after), to find its files as normal.
Note, to truly make a single file, and no PDB, you'll also need:
I found an issue on GitHub here titled
PublishSingleFile excluding appsettings not working as expected
.That pointed to another issue here titled
single file publish: AppContext.BaseDirectory doesn't point to apphost directory
In it, a solution was to try
Process.GetCurrentProcess().MainModule.FileName
The following code configured the application to look at the directory that the single-executable application was run from, rather than the place that the binaries were extracted to.
The
GetBasePath()
implementation: