c# - dotnet core 3.1 windows 服务无法加载配置设置

标签 c# exception .net-core service

我遇到了设计为作为 Windows 服务运行的 dotnet core 3.1 应用程序的问题。该项目源于VS2019中的Worker Service模板。下面是记录到系统事件日志中的调用堆栈。我无法在 SO 上或通过网络搜索找到任何内容。如果我直接(双击)或从命令行执行应用程序,它运行良好。该异常仅在作为 Windows 服务执行时抛出。

Description: The process was terminated due to an unhandled exception.
Exception Info: System.UriFormatException: Invalid URI: The format of the URI could not be determined.
   at System.Uri.CreateThis(String uri, Boolean dontEscape, UriKind uriKind)
   at System.Uri..ctor(String uriString)
   at BranchMonitor.TfvcServer..ctor(IOptions`1 settings) in C:\Users\some_user\source\repos\BranchMonitor\BranchManager\TfvcServer.cs:line 53
   at System.RuntimeMethodHandle.InvokeMethod(Object target, Object[] arguments, Signature sig, Boolean constructor, Boolean wrapExceptions)
   at System.Reflection.RuntimeConstructorInfo.Invoke(BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitConstructor(ConstructorCallSite constructorCallSite, RuntimeResolverContext context)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor`2.VisitCallSiteMain(ServiceCallSite callSite, TArgument argument)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitCache(ServiceCallSite callSite, RuntimeResolverContext context, ServiceProviderEngineScope serviceProviderEngine, RuntimeResolverLock lockType)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitRootCache(ServiceCallSite singletonCallSite, RuntimeResolverContext context)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor`2.VisitCallSite(ServiceCallSite callSite, TArgument argument)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitConstructor(ConstructorCallSite constructorCallSite, RuntimeResolverContext context)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor`2.VisitCallSiteMain(ServiceCallSite callSite, TArgument argument)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitCache(ServiceCallSite callSite, RuntimeResolverContext context, ServiceProviderEngineScope serviceProviderEngine, RuntimeResolverLock lockType)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitRootCache(ServiceCallSite singletonCallSite, RuntimeResolverContext context)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor`2.VisitCallSite(ServiceCallSite callSite, TArgument argument)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitIEnumerable(IEnumerableCallSite enumerableCallSite, RuntimeResolverContext context)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor`2.VisitCallSiteMain(ServiceCallSite callSite, TArgument argument)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitCache(ServiceCallSite callSite, RuntimeResolverContext context, ServiceProviderEngineScope serviceProviderEngine, RuntimeResolverLock lockType)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitRootCache(ServiceCallSite singletonCallSite, RuntimeResolverContext context)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor`2.VisitCallSite(ServiceCallSite callSite, TArgument argument)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.Resolve(ServiceCallSite callSite, ServiceProviderEngineScope scope)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.DynamicServiceProviderEngine.<>c__DisplayClass1_0.<RealizeService>b__0(ServiceProviderEngineScope scope)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.ServiceProviderEngine.GetService(Type serviceType, ServiceProviderEngineScope serviceProviderEngineScope)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.ServiceProviderEngineScope.GetService(Type serviceType)
   at Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetService[T](IServiceProvider provider)
   at Microsoft.Extensions.Hosting.Internal.Host.StartAsync(CancellationToken cancellationToken)
   at Microsoft.Extensions.Hosting.HostingAbstractionsHostExtensions.RunAsync(IHost host, CancellationToken token)
   at Microsoft.Extensions.Hosting.HostingAbstractionsHostExtensions.RunAsync(IHost host, CancellationToken token)
   at Microsoft.Extensions.Hosting.HostingAbstractionsHostExtensions.Run(IHost host)
   at BranchMonitor.Program.Main(String[] args) in C:\Users\some_user\source\repos\BranchMonitor\BranchManager\Program.cs:line 28

这是方法 CreateHostBuilder

public static IHostBuilder CreateHostBuilder(string[] args) =>
     Host.CreateDefaultBuilder(args)
         .ConfigureHostConfiguration(hostBuilder =>
         {
            hostBuilder.SetBasePath(Directory.GetCurrentDirectory());
         })
         .ConfigureAppConfiguration((hostContext, builder) =>
         {
            builder.SetBasePath(Directory.GetCurrentDirectory());
            builder.AddJsonFile("appsettings.json", true, true);
            builder.AddJsonFile($"appsettings.{hostContext.HostingEnvironment.EnvironmentName}.json", true, true);
         })
         .ConfigureServices((hostContext, services) =>
         {
            services.AddHostedService<Worker>();
            services.AddSingleton<ITfvcServer, TfvcServer>();
            services.AddOptions();
            services.Configure<DevOpsSettings>(hostContext.Configuration.GetSection("AzureDevOps"));
         })
         .UseWindowsService();

总结的TfvcServer类是

public class TfvcServer : ITfvcServer
{
   public TfvcServer(IOptions<DevOpsSettings> settings)
   {
      m_server = settings.Value.Server;
      m_collection = settings.Value.Collection;
      m_project = settings.Value.Project;
      m_definitionFolder = settings.Value.BuildDefinitionFolder;

      m_connection = new VssConnection(new Uri($"{m_server}/{m_collection}"),
                         new VssCredentials()
                         {
                            PromptType = CredentialPromptType.DoNotPrompt
                         });

      m_buildClient = m_connection.GetClient<BuildHttpClient>();
      m_tfvcClient = m_connection.GetClient<TfvcHttpClient>();
   }
}

* 编辑 * 问题的根源是 DevOpsSettings 的值为空。在 ctor 内部,我创建了一个失败的 VssConnection 对象,因为服务器(在配置中定义)是一个空字符串。所以我需要弄清楚为什么配置设置为空。 appsettings.Production.json。 “AzureDevOps”部分不在 appsettings.json 中。

{
   "Logging": {
      "LogLevel": {
         "Default": "Information",
         "Microsoft": "Warning",
         "Microsoft.Hosting.Lifetime": "Information"
      }
   },
   "AzureDevOps": {
      "Server": "http://tfsprod:8080/tfs",
      "Collection": "MyCollection",
      "DriveLetters": "KLMNOPSUVWXY",
      "Project": "MyProject",
      "PollingDelay":  60
   }
}

最佳答案

问题是在服务控制管理器 (SCM) 下运行时的当前工作目录 (CWD) 是 C:\Windows\System32(假设默认操作系统安装)。因此,当试图加载配置文件时,它找不到配置文件,因为它不存在。这导致 POCO 设置类的默认设置,在我的例子中是 string.Empty。它在调试器或控制台下工作正常,因为 CWD 是可执行文件的容器文件夹。

有几种方法可以解决这个问题。一个是在 SCM 中配置服务以传递一个参数,该参数是可执行文件的路径。对我来说这不是很干净,因为现在安装需要配置。这会增加复杂性,但没有任何值(value)。

另一种选择是使用

System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location)

而不是调用 Directory.GetCurrentDirectory()。 From this SO question

第三种选择是使用

System.AppDomain.CurrentDomain.BaseDirectory

而不是调用 Directory.GetCurrentDirectory()。 From this blog

我确信还有其他可能的解决方案。目标是提供几个可行的选项,而不是提供详尽的解决方案列表。感谢所有做出贡献的人。我真的很感激你的时间。感谢@JSteward 指出我的大脑放屁让我指出正确的方向来解决问题。我依靠 SO 来解决问题,我感谢那些做出贡献的人。

关于c# - dotnet core 3.1 windows 服务无法加载配置设置,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/61734720/

相关文章:

c# - 重新抛出 ex.InnerException 是个坏主意?

docker - Docker 容器上的 Blazor

C# 自定义窗体在退出时抛出 COMException

c# - 更新和插入查询创建死锁

c# - 使用 ViewModel 方法的命令绑定(bind)

c# - 快速替换Win32_NetworkAdapter WMI类以获取本地计算机的MAC地址

java - 为什么要在 Java 中使用异常处理?

c# - 如何使用 .NET Core 在 Azure Function V3 中使用 IOptions 模式

c# - 如何使用 Novell.Directory.Ldap.NETStandard 和 Simple Paged Results 控件在 Ldap 服务器上进行分页搜索?

c# - 带有很多控件的假滚动容器