dotnet core 3.1 Windows服务无法加载配置设置 [英] dotnet core 3.1 windows service fails to load configuration settings

查看:58
本文介绍了dotnet core 3.1 Windows服务无法加载配置设置的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I've run into a problem with a dotnet core 3.1 application that's designed to run as a windows service. The project originates from the Worker Service template in VS2019. Below is the call stack logged into the system event log. I can't find anything on SO or via web search. If I execute the application directly (double-click) or from a command line it runs fine. The exception is only thrown when executed as a windows service.

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方法

Here's the method 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类是

The TfvcServer class summarized is

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对象,该对象失败了,因为服务器(在config中定义)是一个空字符串.因此,我需要弄清楚为什么配置设置为空.appsettings.Production.json."AzureDevOps"部分不在appsettings.json中.

* EDIT * The source of the problem is the values of the DevOpsSettings are empty. Inside the ctor I create a VssConnection object that is failing since the sever (defined in config) is an empty string. So I need to figure out why the configuration settings are empty. appsettings.Production.json. The "AzureDevOps" section is not in 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是可执行文件的容器文件夹,因此它在调试器或控制台下运行良好.

The problem is the current working directory (CWD) when running under the Service Control Manager (SCM) is C:\Windows\System32 (assuming default OS install). Therefore, when the configuration file is attempted to be loaded it cannot find the configuration file since it doesn't exist. This results in default settings from the POCO settings class, which in my case is string.Empty. It works fine under the debugger or a console since the CWD is the container folder of the executable file.

有几种方法可以解决此问题.一种是在SCM中配置服务以传递参数,即指向可执行文件的路径.对我来说,这不是很干净,因为现在安装需要配置.这导致增加的复杂性而没有价值.

There are a few approaches to solve this. One being configure the service in SCM to pass a parameter being the path to the executable. To me this isn't very clean since now installation requires configuration. This results in added complexity with no value.

另一种选择是使用

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

代替对Directory.GetCurrentDirectory()的调用.来自这个SO问题

instead of the call to Directory.GetCurrentDirectory(). From this SO question

第三个选择是使用

System.AppDomain.CurrentDomain.BaseDirectory

代替对Directory.GetCurrentDirectory()的调用.来自此博客

instead of the call to Directory.GetCurrentDirectory(). From this blog

我确定还有其他可能的解决方案.目标是提供一些可行的方法,而不是详尽的解决方案列表.谢谢所有贡献的人.非常感谢您的宝贵时间.感谢@JSteward指出我的大脑屁,让我朝着正确的方向指出解决方案.我依靠SO来解决问题,并感谢那些做出贡献的人.

I'm sure there are other possible solutions. The goal is to provide a couple options that work rather than an exhaustive list of solutions. Thank you to everyone who contributed. I really appreciate your time. Thanks to @JSteward for pointing out my brain fart to get me pointed in the right direction to a resolution. I rely on SO to solve issues and I appreciate those who contribute.

这篇关于dotnet core 3.1 Windows服务无法加载配置设置的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆