dotnet core TopShelf Windows 服务启动失败
dotnet core TopShelf Windows Service fails to start
我有一个 dotnet 核心控制台应用程序构建来连接到 Sql Service Broker 实例以监视 table 更改。
该应用程序监控一个 table 从 ERP 系统更新,然后将消息发布到我们的总线。
当 运行作为控制台应用程序或在我的 IDE 中调试时 运行 没问题。
我在使用 TopShelf 将其配置为 windows 服务时遇到问题。
这里是入口点:
private static void Main(string[] args)
{
RegisterComponents();
var serviceHost = HostFactory.Run(sc =>
{
sc.Service<ISalesOrderMonitorService>(s =>
{
var sqlListener = _container.ResolveNamed<SqlDependencyEx>(ListenerKey.SalesOrder);
var changeHandler = _container.Resolve<ISalesOrderChangeHandler>();
var listenerConfig = _container.ResolveNamed<ListenerConfiguration>(ListenerKey.SalesOrder);
var logger = _container.Resolve<ILogger<SalesOrder>>();
s.ConstructUsing(f =>
new SalesOrderMonitorService(sqlListener, changeHandler, listenerConfig, logger));
s.WhenStarted(tc => tc.Start());
s.WhenStopped(tc => tc.Stop());
});
});
var exitCode = (int) Convert.ChangeType(serviceHost, serviceHost.GetType());
Environment.ExitCode = exitCode;
}
"worker" class:
public abstract class ServiceBase<T, TZ> : IService<T>
where T : IChangeHandler
{
protected readonly IChangeHandler ChangeHandler;
protected readonly SqlDependencyEx Listener;
protected readonly ListenerConfiguration ListenerConfiguration;
protected readonly ILogger<TZ> Logger;
protected ServiceBase(SqlDependencyEx listener, IChangeHandler changeHandler,
ListenerConfiguration listenerConfiguration, ILogger<TZ> logger)
{
Logger = logger;
ListenerConfiguration = listenerConfiguration;
Listener = listener;
ChangeHandler = changeHandler;
}
public virtual void Start()
{
try
{
Listener.TableChanged += (o, e) => ChangeHandler.Process(e);
Listener.Start();
Logger.LogDebug(
$"Listening to changes on the {ListenerConfiguration.Table} table in the {ListenerConfiguration.Database} database");
}
catch (Exception e)
{
Logger.LogError(e, e.Message);
throw;
}
}
public virtual void Stop()
{
Listener.Stop();
}
通过TopShelf安装没问题:
c:>{ServiceName}.exe install -username "serviceAccount" -password "superSecret" -servicename "ServiceName" -servicedescription "Description" -displayname "Service DisplayName" --autostart
当我开始服务时 - 我得到这个:
这是误导,因为事件查看器显示:
这比 30 秒更快。这肯定与我如何配置 TopShelf 有关。
如前所述 - 该应用程序在 运行 "debug" 或什至只是一个 exe 控制台时工作正常。
我明白了。实际上,来自@DotNetPadawan 和@Lex Li 的评论都间接地把我带到了那里。
对于初学者 - 启用远程调试器让我明白我的 appsetting.json 没有被读入我的 IConfiguration。这真的很令人困惑,因为一切都很好 运行 在本地使用调试器甚至只是启动 exe。
Lex Li 指出的 link 没有提供答案 - 但是那篇文章有这个参考:
Host and Deploy aspnetcore as a Windows Service
我就是在这里发现了这个小金块:
The current working directory returned by calling GetCurrentDirectory for a Windows Service is the C:\WINDOWS\system32 folder. The system32 folder isn't a suitable location to store a service's files (for example, settings files). Use one of the following approaches to maintain and access a service's assets and settings files.
link 解释了如果应用 运行 作为服务,如何有条件地设置当前目录。
var isConsole = args.Contains("-mode:console");
if (!isConsole)
{
var pathToExe = Process.GetCurrentProcess().MainModule?.FileName;
var pathToContentRoot = Path.GetDirectoryName(pathToExe);
Directory.SetCurrentDirectory(pathToContentRoot);
}
为遇到此问题的任何其他人发布此信息。
无可否认 - netcore 3.0 可能是更好的选择 - 但我没有足够的带宽将此存储库(很多共享的东西)升级到 3.0。我需要让它工作。
我有一个 dotnet 核心控制台应用程序构建来连接到 Sql Service Broker 实例以监视 table 更改。
该应用程序监控一个 table 从 ERP 系统更新,然后将消息发布到我们的总线。
当 运行作为控制台应用程序或在我的 IDE 中调试时 运行 没问题。
我在使用 TopShelf 将其配置为 windows 服务时遇到问题。
这里是入口点:
private static void Main(string[] args)
{
RegisterComponents();
var serviceHost = HostFactory.Run(sc =>
{
sc.Service<ISalesOrderMonitorService>(s =>
{
var sqlListener = _container.ResolveNamed<SqlDependencyEx>(ListenerKey.SalesOrder);
var changeHandler = _container.Resolve<ISalesOrderChangeHandler>();
var listenerConfig = _container.ResolveNamed<ListenerConfiguration>(ListenerKey.SalesOrder);
var logger = _container.Resolve<ILogger<SalesOrder>>();
s.ConstructUsing(f =>
new SalesOrderMonitorService(sqlListener, changeHandler, listenerConfig, logger));
s.WhenStarted(tc => tc.Start());
s.WhenStopped(tc => tc.Stop());
});
});
var exitCode = (int) Convert.ChangeType(serviceHost, serviceHost.GetType());
Environment.ExitCode = exitCode;
}
"worker" class:
public abstract class ServiceBase<T, TZ> : IService<T>
where T : IChangeHandler
{
protected readonly IChangeHandler ChangeHandler;
protected readonly SqlDependencyEx Listener;
protected readonly ListenerConfiguration ListenerConfiguration;
protected readonly ILogger<TZ> Logger;
protected ServiceBase(SqlDependencyEx listener, IChangeHandler changeHandler,
ListenerConfiguration listenerConfiguration, ILogger<TZ> logger)
{
Logger = logger;
ListenerConfiguration = listenerConfiguration;
Listener = listener;
ChangeHandler = changeHandler;
}
public virtual void Start()
{
try
{
Listener.TableChanged += (o, e) => ChangeHandler.Process(e);
Listener.Start();
Logger.LogDebug(
$"Listening to changes on the {ListenerConfiguration.Table} table in the {ListenerConfiguration.Database} database");
}
catch (Exception e)
{
Logger.LogError(e, e.Message);
throw;
}
}
public virtual void Stop()
{
Listener.Stop();
}
通过TopShelf安装没问题:
c:>{ServiceName}.exe install -username "serviceAccount" -password "superSecret" -servicename "ServiceName" -servicedescription "Description" -displayname "Service DisplayName" --autostart
当我开始服务时 - 我得到这个:
这是误导,因为事件查看器显示:
这比 30 秒更快。这肯定与我如何配置 TopShelf 有关。
如前所述 - 该应用程序在 运行 "debug" 或什至只是一个 exe 控制台时工作正常。
我明白了。实际上,来自@DotNetPadawan 和@Lex Li 的评论都间接地把我带到了那里。
对于初学者 - 启用远程调试器让我明白我的 appsetting.json 没有被读入我的 IConfiguration。这真的很令人困惑,因为一切都很好 运行 在本地使用调试器甚至只是启动 exe。
Lex Li 指出的 link 没有提供答案 - 但是那篇文章有这个参考:
Host and Deploy aspnetcore as a Windows Service
我就是在这里发现了这个小金块:
The current working directory returned by calling GetCurrentDirectory for a Windows Service is the C:\WINDOWS\system32 folder. The system32 folder isn't a suitable location to store a service's files (for example, settings files). Use one of the following approaches to maintain and access a service's assets and settings files.
link 解释了如果应用 运行 作为服务,如何有条件地设置当前目录。
var isConsole = args.Contains("-mode:console");
if (!isConsole)
{
var pathToExe = Process.GetCurrentProcess().MainModule?.FileName;
var pathToContentRoot = Path.GetDirectoryName(pathToExe);
Directory.SetCurrentDirectory(pathToContentRoot);
}
为遇到此问题的任何其他人发布此信息。
无可否认 - netcore 3.0 可能是更好的选择 - 但我没有足够的带宽将此存储库(很多共享的东西)升级到 3.0。我需要让它工作。