Reputation: 289
I have a simple ASP.NET Core 3.1 running well locally. Trying to run it as a Windows Service, it starts and I am able to interact with controllers but it seems like it is unable to load the appsettings.json
file.
Here is the output of my home controller when running locally:
Hello World running locally - Configuration Minutes: 1 - Server: server-xyz - HeartBeatRunnerClass: grp.csa.soi.soicat.runners.HeartBeatRunner - Env Name: Development - Application: grp.csa.soi.ads.web - Root Path: C:\****\grp.csa.soi.ads.web!!!
Here is the output of my home controller when running in Windows Service:
Hello World running as a Windows Service - Configuration Minutes: 0 - Server: - HeartBeatRunnerClass: - Env Name: Production - Application: grp.csa.soi.ads.web - Root Path: C:\WINDOWS\TEMP\.net\grp.csa.soi.ads.web\rr4fpdsz.c2n\!!!
Here is the relevant part of my Program file:
public static void Main(string[] args)
{
var builder = CreateHostBuilder(args);
if (WindowsServiceHelpers.IsWindowsService())
{
builder.ConfigureHostConfiguration(hostBuilder =>
{
hostBuilder.SetBasePath(System.AppDomain.CurrentDomain.BaseDirectory);
});
}
builder.Build().Run();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
})
.UseWindowsService();
}
Notes:
useWindowsService()
extension method, it is
supposed to setup the content root based on this.appsettings.json
file next to exe
file.I appreciate any pointer.
Regards
Upvotes: 1
Views: 1539
Reputation: 272
I had this problem,too. The clientApp folder with the javascript code was not in C:\WINDOWS\TEMP\xy, too,
In solved both with:
string pathToContentRoot = null;
if (!Debugger.IsAttached) //isService
{
var pathToExe = Process.GetCurrentProcess().MainModule.FileName;
pathToContentRoot = Path.GetDirectoryName(pathToExe); // to find appsettings.json and ClientApp dir when run as windows service
//ausführung als service in: C:\Windows\Temp\.net\a
}
string logFile = pathToContentRoot != null ? $"{pathToContentRoot}\\Logs\\log.log" : "Logs\\log.log";
Log.Logger = new LoggerConfiguration()
.Enrich.FromLogContext()
.WriteTo.Console()
.WriteTo.File(logFile, rollingInterval: RollingInterval.Day, rollOnFileSizeLimit: true, fileSizeLimitBytes: 10000000) //10 mb, max 31 times
.CreateLogger();
Log.Information("Starting up");
var host = Host.CreateDefaultBuilder(args)
.UseWindowsService()
.UseSerilog()
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});
if (pathToContentRoot != null)
{
host.UseContentRoot(pathToContentRoot);
}
host.Build().Run();
And here is my pubxml:
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<WebPublishMethod>FileSystem</WebPublishMethod>
<PublishProvider>FileSystem</PublishProvider>
<LastUsedBuildConfiguration>Release</LastUsedBuildConfiguration>
<LastUsedPlatform>Any CPU</LastUsedPlatform>
<SiteUrlToLaunchAfterPublish />
<LaunchSiteAfterPublish>True</LaunchSiteAfterPublish>
<ExcludeApp_Data>False</ExcludeApp_Data>
<TargetFramework>netcoreapp3.1</TargetFramework>
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
<PublishSingleFile>True</PublishSingleFile>
<ProjectGuid>3cccecb5-d200-4a55-9b9e-6760f1f75fe4</ProjectGuid>
<SelfContained>true</SelfContained>
<publishUrl>bin\Release\netcoreapp3.1\publishResult\</publishUrl>
<DeleteExistingFiles>True</DeleteExistingFiles>
</PropertyGroup>
</Project>
Upvotes: 2
Reputation: 387617
I would suggest you to not use single-file publishing for your Windows services. There are apparently still some issues, e.g. this one, which will prevent the application from knowing exactly where its files are located. This means that the application is not able to locate the appsettings.json
file but instead looks in the folder it was temporarily unpacked to.
Upvotes: 0
Reputation: 398
Here's my approach to this in the past on .NET Core:
For example:
public class Settings
{
public Timer Timer { get; set; }
public Db Database { get; set; }
public Logs Logs { get; set; }
public InventoryFile InventoryFile { get; set; }
}
public class Timer
{
public int MinuteInterval { get; set; }
}
public class Db
{
public string Server { get; set; }
public string Database { get; set; }
public string User { get; set; }
public string Password { get; set; }
public string DebugMode { get; set; }
public string EncryptionKey { get; set; }
}
public class Logs
{
public bool autoDelete { get; set; }
public int autoDeleteMaxDays { get; set; }
public int autoDeleteMaxSize { get; set; }
}
public struct InventoryFile
{
public string FileName { get; set; }
public string FilePath { get; set; }
public string FileExtension { get; set; }
}
Then, read the appsettings.json configuration to the POCO object on the Program.cs file. Like:
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.UseWindowsService()
.ConfigureServices((hostContext, services) =>
{
IConfiguration configuration = hostContext.Configuration;
Settings settings = configuration.GetSection("Settings").Get<Settings>();
services.AddSingleton(settings);
services.AddHostedService<Worker>();
});
This would be my appsettings.json file:
{
"Settings": {
"Timer": {
"MinuteInterval": 1
},
"Database": {
"Server": "192.168.XXX.XXX",
"Database": "MyDbname",
"User": "DbUser",
"Password": "DbPassword",
"DebugMode": true,
"EncryptionKey": "encryptionKey"
},
"Logs": {
"autoDelete": true,
"autoDeleteMaxDays": 0,
"autoDeleteMaxSize": 10
},
"InventoryFile": {
"FileName": "Inventory",
"FilePath": "Inventory/",
"FileExtension": ".csv"
}
}
}
In order to use the POCO object with it's data it has to be passed on to the constructor of the controller or class you need it.
For example, if you are using a WorkerService from .NET Core here's an example on how it would be on the Worker.cs file:
public class Worker : BackgroundService
{
private readonly ILogger<Worker> _logger;
private readonly Settings settings;
public Worker(ILogger<Worker> logger, Settings settings)
{
_logger = logger;
this.settings = settings;
}
Good luck!
Upvotes: 0