简体   繁体   English

如何让我的 .NET Core 3 单文件应用程序找到 appsettings.json 文件?

[英]How can I get my .NET Core 3 single file app to find the appsettings.json file?

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?应如何配置单文件.Net Core 3.0 Web API 应用程序以查找与单文件应用程序构建在同一目录中的appsettings.json文件?

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但是,尝试运行APPNAME.exe会导致以下错误

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.我尝试了一个类似但不同的问题以及其他 Stack Overflow 问题的解决方案。

I attempted to pass the following to SetBasePath()我试图将以下内容传递给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.问题的根源在于PublishSingleFile二进制文件已解压缩并从temp目录运行。

In the case of this single file app, the location it was looking appsettings.json was the following directory:对于这个单文件应用程序,它查找appsettings.json的位置是以下目录:

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.上述所有方法都指向文件解压缩到的位置,与运行它的位置不同。

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.如果您可以在运行时在可执行文件之外使用文件,那么您可以在 csproj 中标记您想要的文件。 This method allows for live changes and such in a known location.此方法允许在已知位置进行实时更改等。

<ItemGroup>
    <None Include="appsettings.json">
      <CopyToOutputDirectory>Always</CopyToOutputDirectory>
      <CopyToPublishDirectory>Always</CopyToPublishDirectory>
      <ExcludeFromSingleFile>true</ExcludeFromSingleFile>
    </None>
    <None Include="appsettings.Development.json;appsettings.QA.json;appsettings.Production.json;">
      <CopyToOutputDirectory>Always</CopyToOutputDirectory>
      <CopyToPublishDirectory>Always</CopyToPublishDirectory>
      <DependentUpon>appsettings.json</DependentUpon>
      <ExcludeFromSingleFile>true</ExcludeFromSingleFile>
    </None>
  </ItemGroup>

  <ItemGroup>
    <None Include="Views\Test.cshtml">
      <CopyToOutputDirectory>Always</CopyToOutputDirectory>
      <ExcludeFromSingleFile>true</ExcludeFromSingleFile>
    </None>
  </ItemGroup>

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.这允许配置和 razor(我在之后添加)正常查找其文件。

// when using single file exe, the hosts config loader defaults to GetCurrentDirectory
            // which is where the exe is, not where the bundle (with appsettings) has been extracted.
            // when running in debug (from output folder) there is effectively no difference
            var realPath = Directory.GetParent(System.Reflection.Assembly.GetExecutingAssembly().Location).FullName;

            var host = Host.CreateDefaultBuilder(args).UseContentRoot(realPath);

Note, to truly make a single file, and no PDB, you'll also need:请注意,要真正制作单个文件而不是 PDB,您还需要:

<DebugType>None</DebugType>

I found an issue on GitHub here titled PublishSingleFile excluding appsettings not working as expected .我在GitHub上发现了一个问题,标题为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这指向了另一个名为单个文件发布的问题single file publish: AppContext.BaseDirectory doesn't point to apphost directory

In it, a solution was to try Process.GetCurrentProcess().MainModule.FileName在其中,一个解决方案是尝试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.以下代码将应用程序配置为查看运行单可执行应用程序的目录,而不是提取二进制文件的位置。

config.SetBasePath(GetBasePath());
config.AddJsonFile("appsettings.json", false);

The GetBasePath() implementation: GetBasePath()实现:

private string GetBasePath()
{
    using var processModule = Process.GetCurrentProcess().MainModule;
    return Path.GetDirectoryName(processModule?.FileName);
}

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).我的应用程序在 .NET Core 3.1 上,作为单个文件发布并作为 Windows 服务运行(这可能会或可能不会对问题产生影响)。

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:Process.GetCurrentProcess().MainModule.FileName作为内容根的建议解决方案对我有用,但前提是我将内容根设置在正确的位置:

This works:这有效:

Host.CreateDefaultBuilder(args)
    .UseWindowsService()
    .ConfigureWebHostDefaults(webBuilder =>
    {
        webBuilder.UseContentRoot(...);
        webBuilder.UseStartup<Startup>();
    });

This does not work:这不起作用:

Host.CreateDefaultBuilder(args)
    .UseWindowsService()
    .UseContentRoot(...)
    .ConfigureWebHostDefaults(webBuilder =>
    {
        webBuilder.UseStartup<Startup>();
    });

this is the "piggy back" answer(s) area.这是“背负式”答案区域。

First, upvote the above answer by "RS" that I reference in this answer.首先,通过我在此答案中引用的“RS”来支持上述答案。 That was the magic.这就是魔法。

Short answer is "use RS's answer AND set that value in all the right places.".简短的回答是“使用 RS 的答案并在所有正确的地方设置该值。”。 I show the 2 places to SET the values below.我展示了设置以下值的 2 个位置。

My specific ADDITION (not mentioned anywhere else) is:我的具体补充(其他地方没有提到)是:

            IConfigurationBuilder builder = new ConfigurationBuilder()
            /* IMPORTANT line below */
                    .SetBasePath(realPath)

Longer answer is:更长的答案是:

I needed the above answers AND I have some additions.我需要上面的答案,我还有一些补充。

In my output (i'll show code later), here is the difference between the 2 answers above.在我的 output (我稍后会显示代码)中,这是上面两个答案之间的区别。

    GetBasePath='/mybuilddir/myOut'

  
  realPath='/var/tmp/.net/MyCompany.MyExamples.WorkerServiceExampleOne.ConsoleOne/jhvc5zwc.g25'

where '/mybuilddir/myOut' was the location here I published my single file..in my docker definition file.其中“/mybuilddir/myOut”是我发布我的单个文件的位置。在我的 docker 定义文件中。

GetBasePath did NOT work when using PublishSingleFile使用 PublishSingleFile 时 GetBasePath 不起作用

"realPath" was the way I finally got it to work. “realPath”是我最终让它工作的方式。 Aka, the answer above.嗯,上面的答案。 : How can I get my .NET Core 3 single file app to find the appsettings.json file? : 如何让我的 .NET Core 3 单文件应用程序找到 appsettings.json 文件?

and when you see the value of "realPath"...then it all makes sense.当你看到“realPath”的价值时……这一切都说得通了。 the singleFile is being extracted ~somewhere....and RS figured out the magic sauce on where that extraction place is.单个文件正在被提取~某处....并且 RS 找出了提取位置所在的魔法酱。

I will show my entire Program.cs, that will give context to everything.我将展示我的整个 Program.cs,这将为所有内容提供上下文。

Note, I had to set "realPath" in TWO places.注意,我必须在两个地方设置“realPath”。

I marked the important things with我用

/* IMPORTANT

Full code below, which (again) borrows from RS's answer: How can I get my .NET Core 3 single file app to find the appsettings.json file?下面的完整代码(再次)借鉴了 RS 的答案: 如何让我的 .NET Core 3 单文件应用程序找到 appsettings.json 文件?

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;

using Serilog;

namespace MyCompany.MyExamples.WorkerServiceExampleOne.ConsoleOne
{
    public static class Program
    {
        public static async Task<int> Main(string[] args)
        {
            /* easy concrete logger that uses a file for demos */
            Serilog.ILogger lgr = new Serilog.LoggerConfiguration()
                .WriteTo.Console()
                .WriteTo.File("MyCompany.MyExamples.WorkerServiceExampleOne.ConsoleOne.log.txt", rollingInterval: Serilog.RollingInterval.Day)
                .CreateLogger();

            try
            {
                /* look at the Project-Properties/Debug(Tab) for this environment variable */
                string environmentName = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT");
                Console.WriteLine(string.Format("ASPNETCORE_ENVIRONMENT='{0}'", environmentName));
                Console.WriteLine(string.Empty);

                string basePath = Directory.GetCurrentDirectory();
                basePath = GetBasePath();

                Console.WriteLine(string.Format("GetBasePath='{0}'", basePath));
                Console.WriteLine(string.Empty);

                // when using single file exe, the hosts config loader defaults to GetCurrentDirectory
                // which is where the exe is, not where the bundle (with appsettings) has been extracted.
                // when running in debug (from output folder) there is effectively no difference
                /* IMPORTANT 3 lines below */
                string realPath = Directory.GetParent(System.Reflection.Assembly.GetExecutingAssembly().Location).FullName;
                Console.WriteLine(string.Format("realPath='{0}'", realPath));
                Console.WriteLine(string.Empty);


                IConfigurationBuilder builder = new ConfigurationBuilder()
                /* IMPORTANT line below */
                        .SetBasePath(realPath)
                        .AddJsonFile("appsettings.json")
                        .AddJsonFile($"appsettings.{environmentName}.json", true, true)
                        .AddEnvironmentVariables();

                IConfigurationRoot configuration = builder.Build();


                IHost host = Host.CreateDefaultBuilder(args)
                /* IMPORTANT line below */
                      .UseContentRoot(realPath)
                    .UseSystemd()
                    .ConfigureServices((hostContext, services) => AppendDi(services, configuration, lgr)).Build();

                await host.StartAsync();

                await host.WaitForShutdownAsync();
            }
            catch (Exception ex)
            {
                string flattenMsg = GenerateFullFlatMessage(ex, true);
                Console.WriteLine(flattenMsg);
            }

            Console.WriteLine("Press ENTER to exit");
            Console.ReadLine();

            return 0;
        }

        private static string GetBasePath()
        {
            using var processModule = System.Diagnostics.Process.GetCurrentProcess().MainModule;
            return Path.GetDirectoryName(processModule?.FileName);
        }

        private static string GenerateFullFlatMessage(Exception ex)
        {
            return GenerateFullFlatMessage(ex, false);
        }

        private static void AppendDi(IServiceCollection servColl, IConfiguration configuration, Serilog.ILogger lgr)
        {
            servColl
                .AddSingleton(lgr)
                .AddLogging();

            servColl.AddHostedService<TimedHostedService>(); /* from https://docs.microsoft.com/en-us/aspnet/core/fundamentals/host/hosted-services?view=aspnetcore-3.1&tabs=visual-studio and/or https://github.com/dotnet/AspNetCore.Docs/blob/master/aspnetcore/fundamentals/host/hosted-services/samples/3.x/BackgroundTasksSample/Services/TimedHostedService.cs */

            servColl.AddLogging(blder =>
            {
                blder.AddConsole().SetMinimumLevel(LogLevel.Trace);
                blder.SetMinimumLevel(LogLevel.Trace);
                blder.AddSerilog(logger: lgr, dispose: true);
            });

            Console.WriteLine("Using UseInMemoryDatabase");
            servColl.AddDbContext<WorkerServiceExampleOneDbContext>(options => options.UseInMemoryDatabase(databaseName: "WorkerServiceExampleOneInMemoryDatabase"));
        }

        private static string GenerateFullFlatMessage(Exception ex, bool showStackTrace)
        {
            string returnValue;

            StringBuilder sb = new StringBuilder();
            Exception nestedEx = ex;

            while (nestedEx != null)
            {
                if (!string.IsNullOrEmpty(nestedEx.Message))
                {
                    sb.Append(nestedEx.Message + System.Environment.NewLine);
                }

                if (showStackTrace && !string.IsNullOrEmpty(nestedEx.StackTrace))
                {
                    sb.Append(nestedEx.StackTrace + System.Environment.NewLine);
                }

                if (ex is AggregateException)
                {
                    AggregateException ae = ex as AggregateException;

                    foreach (Exception aeflatEx in ae.Flatten().InnerExceptions)
                    {
                        if (!string.IsNullOrEmpty(aeflatEx.Message))
                        {
                            sb.Append(aeflatEx.Message + System.Environment.NewLine);
                        }

                        if (showStackTrace && !string.IsNullOrEmpty(aeflatEx.StackTrace))
                        {
                            sb.Append(aeflatEx.StackTrace + System.Environment.NewLine);
                        }
                    }
                }

                nestedEx = nestedEx.InnerException;
            }

            returnValue = sb.ToString();

            return returnValue;
        }
    }
}

and my toplayer csproj contents:和我的顶层 csproj 内容:

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>netcoreapp3.1</TargetFramework>
  </PropertyGroup>

  <ItemGroup>
    <!-- allows one line of code to get a txt file logger #simple #notForProduction -->
    <PackageReference Include="Serilog" Version="2.9.0" />
    <PackageReference Include="Serilog.Sinks.Console" Version="3.1.1" />
    <PackageReference Include="Serilog.Sinks.File" Version="4.1.0" />
    <PackageReference Include="Serilog.Extensions.Logging" Version="3.0.1" />
  </ItemGroup>

  <ItemGroup>
    <PackageReference Include="Microsoft.Extensions.Logging.Console" Version="3.1.6" />
    <PackageReference Include="Microsoft.EntityFrameworkCore.InMemory" Version="3.1.6" />
    <PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="3.1.6" />
    <PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="3.1.6" />
    <PackageReference Include="Microsoft.Extensions.Hosting.Systemd" Version="3.1.6" />
  </ItemGroup>



  <ItemGroup>
    <None Update="appsettings.Development.json">
      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
    </None>
    <None Update="appsettings.json">
      <CopyToOutputDirectory>Always</CopyToOutputDirectory>
    </None>
  </ItemGroup>



</Project>

and my docker file for kicks:和我的 docker 文件用于踢球:

# See https://hub.docker.com/_/microsoft-dotnet-core-sdk/
FROM mcr.microsoft.com/dotnet/core/sdk:3.1 AS buildImage
WORKDIR /mybuilddir


# Copy sln and csprojs and restore as distinct layers
COPY ./src/Solutions/MyCompany.MyExamples.WorkerServiceExampleOne.sln ./src/Solutions/

COPY ./src/ConsoleOne/*.csproj ./src/ConsoleOne/


RUN dotnet restore ./src/Solutions/MyCompany.MyExamples.WorkerServiceExampleOne.sln

COPY ./src ./src



RUN dotnet publish "./src/ConsoleOne/MyCompany.MyExamples.WorkerServiceExampleOne.ConsoleOne.csproj" -c Release -o myOut -r linux-x64 /p:PublishSingleFile=true /p:DebugType=None  --framework netcoreapp3.1

# See https://hub.docker.com/_/microsoft-dotnet-core-runtime/
FROM mcr.microsoft.com/dotnet/core/runtime:3.1 AS runtime
WORKDIR /myrundir
COPY --from=buildImage /mybuilddir/myOut ./

# this line is wrong for  PublishSingleFile  ### ENTRYPOINT ["dotnet", "MyCompany.MyExamples.WorkerServiceExampleOne.ConsoleOne.dll"]

#below is probably right...i was still working on this at time of posting this answer
 ./myOut/MyCompany.MyExamples.WorkerServiceExampleOne.ConsoleOne

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 在独立的 .NET Core DLL 中,如何从 appsettings.json 配置文件中获取值? - In a stand-alone .NET Core DLL, how do I get a value from the appsettings.json config file? 为什么我的 .net core xunit 测试找不到我的 appsettings.json? - Why can't my .net core xunit test find my appsettings.json? Docker 中 .NET Core 应用程序的 appSettings.json? - appSettings.json for .NET Core app in Docker? 读取appsettings.json文件C#.NET Core时出现问题 - Issues reading appsettings.json file C# .NET Core 在 .NET 核心的 cshtml 文件中显示 appsettings.json 值 - Show appsettings.json value in cshtml file in .NET Core 如何摆脱错误“无效的 appsettings.json 文件? - How can I get rid of the Error "Invalid appsettings.json file? 如何获取 appsettings.json 文件中 Azure 托管的数据库的连接字符串? - How can I get the connection string to a database hosted Azure in the appsettings.json file? 在 .NET 6 控制台应用程序中读取 appsettings.json 文件 - Reading appsettings.json file in .NET 6 console app 如何覆盖 appsettings.json 或 appsettings。<envrionment> .json 文件以编程方式使用 .net 核心 web api 和 C#?</envrionment> - How to overrite appsettings.json or appsettings.<envrionment>.json file(s) programtically using .net core web api with C#? 如何将ConnectionString从appsettings.json文件传递到dot net core 2.2中的类库项目 - How to pass ConnectionString from appsettings.json file to class library project in dot net core 2.2
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM