8

I created a .net core worker service, had IConfiguration injected into it so I can read a property from appsettings.json but everytime I run the file the property comes up as null. However if I force the IConfiguration to add a json file via the ConfigurationBuilder it works. How can I use the default settings to allow .net core worker service to read from appsettings?

public class ImageFileWatcher : IHostedService, IDisposable
{

    private readonly ILogger _logger;
    private readonly IConfiguration _configuration;
    FileSystemWatcher _watcher;

    public ImageFileWatcher(ILogger<ImageFileWatcher> logger, IConfiguration configuration)
    {
        _logger = logger;
        _configuration = configuration;
        // IConfigurationBuilder builder = new ConfigurationBuilder()
        // .AddJsonFile("appsettings.json")
        // .AddEnvironmentVariables();
        // this._configuration = builder.Build();
    }

    public Task StartAsync(CancellationToken cancellationToken)
    {

        var watcherConfig = _configuration["WatcherConfig:filePath"];//this is where it comes up as null.
        _logger.LogInformation($"Image File watcher started on path{watcherConfig}");
        _watcher = new FileSystemWatcher(watcherConfig);
        _watcher.Created += OnNewImage;
        _watcher.EnableRaisingEvents = true;
        return Task.CompletedTask;
    }

Here is the appsettings.json file

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft": "Warning",
      "Microsoft.Hosting.Lifetime": "Information"
    }
  },
  "WatcherConfig":{

    "filePath":"/Users/###/Documents/Development/ImageDrop"
  }
  
}

Here is the Program.cs file

public class Program
    {
        public static void Main(string[] args)
        {
            CreateHostBuilder(args).Build().Run();
        }

        public static IHostBuilder CreateHostBuilder(string[] args) =>
            Host.CreateDefaultBuilder(args)
                .ConfigureAppConfiguration((hostContext, configBuilder) =>
                {


                    configBuilder
                    .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
                    .AddJsonFile("appsettings.Production.json", optional: true, reloadOnChange: true)
                    .AddEnvironmentVariables(prefix: "ImageService_")
                    .Build();

                })
                .ConfigureServices((hostContext, services) =>
                {

                    services.AddHostedService<ImageFileWatcher>();

                });
    }

Here is the second attempt at the Program class:

public class Program
    {
        public static void Main(string[] args)
        {
            CreateHostBuilder(args).Build().Run();
        }

        public static IHostBuilder CreateHostBuilder(string[] args) =>
            Host.CreateDefaultBuilder(args)

                .ConfigureServices((hostContext, services) =>
                {
                    IConfiguration builder = new ConfigurationBuilder()
                     .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
                    .AddJsonFile("appsettings.Production.json", optional: true, reloadOnChange: true)
                    .AddEnvironmentVariables(prefix: "ImageService_")
                    .Build();

                    hostContext.Configuration = builder;
                    services.AddHostedService<ImageFileWatcher>();

                });
    }
Progman
  • 16,827
  • 6
  • 33
  • 48
guimar86
  • 81
  • 1
  • 1
  • 4

3 Answers3

4

I've also faced this problem in a similar approach.

Glad "dotnet clean" and "dotnet build" worked for you.

Below, just wanna to contribute with a different solution.

  • Right button on appsettings.json > Properties > Copy to Output Directory > Copy always

  • Program


     static class Program
        {
            public static IConfigurationRoot Configuration { get; private set; }
    
            static async Task Main(string[] args)
            {
                using IHost host = CreateHostBuilder(args).Build();
    
            }
    
            static IHostBuilder CreateHostBuilder(string[] args) =>
              Host.CreateDefaultBuilder(args)
               .ConfigureAppConfiguration((hostingContext, configuration) =>
                {
                    configuration.Sources.Clear();
    
                    configuration
                            .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true);
    
                    IConfigurationRoot configurationRoot = configuration.Build();
    
                    Configuration = configurationRoot;
                }).ConfigureServices((services) =>
                {
                    services.RegisterServices();
                });
        }

  • Created a class called AppSettings and other class called EndPoint to interact with appsettings.json data

    public class EndPoint
        {
            public string UrlWebApi { get; set; }
         }
    
        public class AppSettings
        {
            public EndPoint EndPoints { get; set; }
    
            public string Application { get; set; }
           
            public AppSettings()
            {
                IConfigurationBuilder builder = new ConfigurationBuilder();
    
                builder.AddJsonFile(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "appsettings.json"));
    
                var root = builder.Build();
    
                root.Bind(this);
            }
        }

  • Example of use of Appsettings class gettings URL from appsettings.json

    public class MyAppService : IMyAppService
        {
            private readonly AppSettings _appSettings;
            private readonly ILogger<MyAppService> _logger;
    
            public MyAppService(AppSettings appSettings, ILogger<MyAppService> logger)
            {
                _appSettings = appSettings;
                _logger = logger;
            }
    
            public async Task<string> ShowAppSettingsUse()
            {
                    _logger.LogInformation("Getting endpoint URL from AppSettings...");
    
                    var urlFromAppSettings = _appSettings.EndPoints.UrlWebApi;
    
                    return urlFromAppSettings;
            }
        }
    }
  • Thanks for the contribution, I forgot to mention I am working on VsCode on a mac so the steps to Copy to Output Directory are a bit different. – guimar86 Jan 10 '22 at 17:06
  • The following step did the trick for me. "appsettings.json > Properties > Copy to Output Directory > Copy always" Thanks. Upvoted. – Mukesh Kumar Jan 11 '23 at 18:31
3

Given that you are using the default appsettings.json setup, the use of CreateDefaultBuilder() should mean you have nothing extra to configure.

There's a couple of notes which may help:

  • Remove any custom configuration setup as you don't need it (because of CreateDefaultBuilder)

  • Make sure that the appsettings.json file is copied to the build output. If not, in Visual Studio, you can right-click on the file and set "Copy to Output Directory" to "Copy if newer".

Alternatively, if you want to configure manually, note that:

  • When configuring the host, adding your config providers, you don't need to call the Build() method.

  • In your second example of Program the configuration is being set up in ConfigureServices, which is not where it should be. I guess this is why it was necessary to use the line hostContext.Configuration = builder which should not be required.

Matt Tester
  • 4,663
  • 4
  • 29
  • 32
  • 1
    By removing all extra configurations, I simpply used the commands "dotnet clean" and "dotnet build" and now it works. Sadly now, I now cannot reproduce the error. – guimar86 Jan 10 '22 at 00:33
  • 1
    Good news that it is working for you now, although annoying when that happens! :) When you come to adding any custom providers, ensuring they are in the `ConfigureAppConfiguration()` method (and with no call to `Build()`), you should have success. – Matt Tester Jan 10 '22 at 00:36
  • For anyone else still having this issue, somehow VS was executing the right binaries but in the wrong directory context. Restarting VS fixed the issue. – nh43de Sep 10 '22 at 13:41
2

You must read App settings in Program.cs while building Host service. Check out what Host.CreateDefaultBuilder does, and if you decide to not use the default builder you might need to load the settings implicit.

If you heave further questions, please attach the Program.cs file, as the IConfiguration depends on its initialization not the usage in the service.

  • I have edited the post and added the Program Class so you can see and both approaches still have the configuration data dictionary with a count of 0. It's as if it's not finding the files – guimar86 Jan 09 '22 at 20:12
  • You create your configuration while configuring services and assignee build configuration into the Host. I believe that this might be invoked after registration of IConfiguration in DI container. Wich means that you have two different instances of IConfiguration (one assigned to host, another one registered in DI). A place to customize your configuration is ConfigureAppConfiguration() method. – Rafał Panasewicz Jan 09 '22 at 22:58
  • Yeah, but since this project already brings an appsettings.json and the Microsoft.Extensions.Hosting nuget, I shouldn't need any further configuration. Strangely, after doing the commands "dotnet clean" and "dotnet build" the project now seems to work. – guimar86 Jan 10 '22 at 00:35