10

I am trying to run my ASP.Net Core 2.1 application as a service on Windows 10. My application runs fine when ran using VS2017 or if I publish to a folder and then start it from within that published folder with the --console arg. However, if I use sc create to create the service, getting a Success result, and then try to run it, I get an error in the Windows Application log stating...

System.IO.FileNotFoundException: The configuration file 'appsettings.json' was not found and is not optional. The physical path is 'C:\WINDOWS\system32\appsettings.json'.
at Microsoft.Extensions.Configuration.FileConfigurationProvider.Load(Boolean reload)   

I have confirmed that my appsettings.json file exists in the published folder. The error message states that is is looking in the C:\WINDOWS\system32\ folder for the appsettings.json file rather than the published folder where the application .exe resides. This leads me to think the issue is with the way the current directory is being set when ran as a service, but I am having trouble identifying exactly why it is not working.

I have set up my Program.cs by following the instructions in this Microsoft document. My program.cs is shown below;

public class Program
{

    public static IConfiguration Configuration { get; } = new ConfigurationBuilder()
        .SetBasePath(Directory.GetCurrentDirectory())
        .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
        .AddJsonFile($"appsettings.{Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") ?? "Production"}.json", optional: true)
        .AddUserSecrets<Startup>()
        .Build();

    public static void Main(string[] args)
    {
        ConfigureSerilog();


        // Set up to run as a service if not in Debug mode or if a command line argument is not --console
        var isService = !(Debugger.IsAttached || args.Contains("--console"));
        if (isService)
        {
            var processModule = Process.GetCurrentProcess().MainModule;
            if (processModule != null)
            {
                var pathToExe = processModule.FileName;
                var pathToContentRoot = Path.GetDirectoryName(pathToExe);
                Directory.SetCurrentDirectory(pathToContentRoot);
            }
        }

        var builder = CreateWebHostBuilder(args.Where(arg => arg != "--console").ToArray());
        var host = builder.Build();
        if (isService)
        {
            host.RunAsCustomService();
        }
        else
        {
            try
            {
                Log.Information("Starting CAS API in Program.cs");
                host.Run();
            }
            catch (Exception ex)
            {
                Log.Fatal(ex, "CAS API Host terminated unexpectedly");
            }
            finally
            {
                Log.CloseAndFlush();
            }
        }
    }

    public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
        WebHost.CreateDefaultBuilder(args)
            .ConfigureLogging((hostingContext, logging) => { logging.AddEventSourceLogger(); })
            .ConfigureAppConfiguration((context, config) =>
            {
                // Configure the app here.
            })
            .UseStartup<Startup>()
            .UseSerilog()
            .UseUrls("https://localhost:60026"); //This is only used when ran as a stand-alone service. Not in VisualStudio

    private static void ConfigureSerilog()
    {
        // Set up Serilog
        var connectionString = Configuration.GetConnectionString("CasDbConnectionString");
        const string tableName = "Logs";

        Log.Logger = new LoggerConfiguration()
            .MinimumLevel.Information()
            .MinimumLevel.Override("Microsoft", LogEventLevel.Warning)
            .Filter.ByExcluding(Matching.FromSource("Microsoft.EntityFrameworkCore.Query"))
            .Enrich.FromLogContext()
            .Enrich.WithMachineName()
            .Enrich.WithThreadId()
            .Enrich.WithUtcTimeStamp()
            .Enrich.WithProperty("Application", $"{System.Reflection.Assembly.GetExecutingAssembly().GetName().Name}")
            .Enrich.FromLogContext()
            .CreateLogger();

    }

I have tried replacing the...

host.RunAsCustomeService();

with ...

host.RunAsService();

And I still had the same problem.

I am running the sc command as follows;

sc create casapi start= auto binPath= c:\deployments\casapi\casapi.exe

And I see the casapi service listed in Windows Services. But if I run the sc command...

sc start casapi 

I get the error indicated above.

If I go to the published folder in an elevated command prompt and type... casapi.exe --console The application runs as expected.

The casapi service is installed to Log On as a local system account.

3
  • think you need to put appsettings.json in bin folder in that scenario Commented Jul 9, 2019 at 15:52
  • @JoeAudette, A bin folder below the published folder? in my case c:\deployments\casapi\bin? I just tried that and it made no difference. I would think that, if that was required, the Publish to a folder in VS2017 would have done so. Commented Jul 9, 2019 at 17:06
  • You might also have to include appsettings.json as an embedded file. Commented Jul 9, 2019 at 23:38

3 Answers 3

13

As App configuration, we need to call SetCurrentDirectory and use a path to the app's published location.

For your issue, you access Directory.GetCurrentDirectory() before calling Directory.SetCurrentDirectory(pathToContentRoot); as you call ConfigureSerilog(); first.

Try to change the order like

    // Set up to run as a service if not in Debug mode or if a command line argument is not --console
    var isService = !(Debugger.IsAttached || args.Contains("--console"));
    if (isService)
    {
        var processModule = Process.GetCurrentProcess().MainModule;
        if (processModule != null)
        {
            var pathToExe = processModule.FileName;
            var pathToContentRoot = Path.GetDirectoryName(pathToExe);
            Directory.SetCurrentDirectory(pathToContentRoot);
        }
    }
    ConfigureSerilog();
Sign up to request clarification or add additional context in comments.

Comments

6

Try this.

Directory.SetCurrentDirectory(AppDomain.CurrentDomain.BaseDirectory);

2 Comments

I'm running a Blazor application as a windows service. I originally set the current directory to the assembly directory. This worked for the service, but later when trying to do migrations, the path returned was the NuGet package location for EF tools. AppDomain.CurrentDomain.BaseDirectory allowed it to work in all situations.
5

If you're using Task Scheduler, you can write the directory you want into

Action Tab --> Edit --> Start in

So the program will start running from that directory. enter image description here

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.