Search code examples
c#visual-studioappsettingsioptionsmonitor

IOptionsMonitor populates when app run from Visual Studio but not when run from .exe


I am experiencing an issue where options loaded from config into constructors via IOptionsMonitor are populated when I run my application in Visual Studio, but NOT populated if I run the built .exe from a command prompt.

I must be missing some fundamental thing to do with config but I just can't work it out.

Any help would be appreciated as I'm at a loss and have been struggling with this for some time now....

I have put together the code at the bottom to show what I am seeing.

When I run this in Visual Studio, the output is:

enter image description here

When I run the built .exe in a command window, the output is:

enter image description here

using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Options;

namespace ConsoleApp1;

public static class Program
{
    #region Public interface 

    public static void Main()
    {
        try
        {
            var host = BuildHost();

            var test = host.Services.GetRequiredService<Consumer>();

            test.DoSomething();
        }
        finally
        {
            Console.ReadLine();
        }
    }

    #endregion

    #region Private interface 

    private static IHost BuildHost()
    {
        var host = Host.CreateDefaultBuilder()
            .ConfigureServices((context, services) =>
            {
                services.Configure<SampleOptions>(context.Configuration.GetSection("SampleOptions"));
                services.AddSingleton<Consumer, Consumer>();
            })
            .Build();

        return host;
    }

    #endregion
}

public class SampleOptions
{
    public int? IntegerProperty { get; set; }
}

public class Consumer
{
    public Consumer(IOptionsMonitor<SampleOptions> optionsMonitor)
    {
        OptionsMonitor = optionsMonitor;
    }

    private IOptionsMonitor<SampleOptions> OptionsMonitor { get; }

    public void DoSomething()
    {
        var options = OptionsMonitor.CurrentValue;

        if (!options.IntegerProperty.HasValue)
            Console.WriteLine("options.IntegerProperty is NULL");
        else
            Console.WriteLine("options.IntegerProperty is not NULL");
    }
}

I have an "appsettings.json" file as per below (set to "Copy If Newer"):

{
    "SampleOptions": {
        "IntegerProperty": 1
    }
}

UPDATE: HostingEnvironment

I added the code below and that confirmed that the issue is to do with the current directory as suggested in the comments:

    var hostingEnvironment = host.Services.GetRequiredService<IHostEnvironment>();

    Console.WriteLine($"hostingEnvironment.EnvironmentName: {hostingEnvironment.EnvironmentName}");
    Console.WriteLine($"hostingEnvironment.ContentRootPath: {hostingEnvironment.ContentRootPath}");

Command window output:

enter image description here

UPDATE:

The fix was to set the ContentRoot:

private static IHost BuildHost()
{
    return Host.CreateDefaultBuilder()
        .UseContentRoot(AppContext.BaseDirectory)
        .ConfigureServices((context, services) =>
        {
            services.Configure<SampleOptions>(context.Configuration.GetSection("SampleOptions"));
            services.AddSingleton<Consumer, Consumer>();
        })
        .Build();
}

Case closed.


Solution

  • Reference from J M's solution:

    Need to add .UseContentRoot(AppContext.BaseDirectory)

    like this:

    private static IHost BuildHost()
    {
        return Host.CreateDefaultBuilder()
            .UseContentRoot(AppContext.BaseDirectory)
            .ConfigureServices((context, services) =>
            {
                services.Configure<SampleOptions>(context.Configuration.GetSection("SampleOptions"));
                services.AddSingleton<Consumer, Consumer>();
            })
            .Build();
    }