Home > Blockchain >  Options Pattern Class not Populating
Options Pattern Class not Populating

Time:12-28

I'm creating a .NET 6 worker service, hosted as a windows service. The service uses the FluentFTP library to download files from a remote server. I'm trying to implement strongly-typed configuration using the Options Pattern but am having difficulties with the binding.

public class Program
{      
    public static async Task Main(string[] args)
    {
        var config = new ConfigurationBuilder().Build();
        var logger = LogManager.Setup()
            .SetupExtensions(ext => ext.RegisterConfigSettings(config))
            .GetCurrentClassLogger();
        
        IHost host = Host.CreateDefaultBuilder(args)
            .UseWindowsService(Options => { Options.ServiceName = "Foo FTP File Process"; })
            .ConfigureAppConfiguration((hostingContext, config) =>
            {
                config.Sources.Clear();
                IHostEnvironment env = hostingContext.HostingEnvironment;
        
                config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
                    .AddJsonFile($"appsettings.{env.EnvironmentName}.json", true, true);
                IConfigurationRoot configurationRoot = config.Build();
        
                FTPSettingsOptions options = new();
                configurationRoot.GetSection(nameof(FTPSettingsOptions))
                    .Bind(options);

                //Working
                Console.WriteLine($"FTPSettingsOptions.Enabled={options.FTPUsername}");
            })
            .ConfigureServices(services =>
            {
                services.AddOptions();                   
                services.AddSingleton<IFTPService, FTPService>();
                services.AddHostedService<Worker>();
            })
            .ConfigureLogging((hostContext, logging) =>
            {
                logging.ClearProviders();
                logging.AddNLog(hostContext.Configuration, new NLogProviderOptions() 
                { LoggingConfigurationSectionName = "NLog" });
                logging.AddConsole();
                logging.SetMinimumLevel(Microsoft.Extensions.Logging.LogLevel.Trace);
            })
            .Build();
                    
        await host.RunAsync();
    }
}

appSettings.Development.json:

{
  "FTPSettingsOptions": {
    "FTPUri": "ftp://ftp.foo.com",
    "FTPUsername": "footest",
    "FTPPassword": "***********",
    "FTPServerPath": "/home/footest/foo",
    "CallServerPath": "C:\\Temp\\foo"
  },
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.Hosting.Lifetime": "Information"
    }
  }
}

POCO Class:

public class FTPSettingsOptions
{
    public FTPSettingsOptions() { }

    public const string FTPSettings = "FTPSettings";

    public string FTPUri { get; set; } = String.Empty;
    public string FTPUsername { get; set; } = String.Empty;
    public string FTPPassword { get; set; } = String.Empty;
    public string FTPServerPath { get; set; } = String.Empty;
    public string CallServerPath { get; set; } = String.Empty;
}

This is where the options values are coming in as null:

public class FTPService : IFTPService
{
    private readonly IOptions<FTPSettingsOptions> _options;
    
    public FTPService(IOptions<FTPSettingsOptions> options)
    {
        FTPSettingsOptions _options = options.Value;
        
        // Connect only once (Singleton scope)
        Connect();
    }

    public void Connect()
    {
        // FTPUsername and FTPPassword have no values
        using ( var conn = new FtpClient("ftp://ftp.foo.com", _options.Value.FTPUsername,                           
            _options.Value.FTPPassword))
        {
            ...
            conn.Connect();
        }
    }
}

CodePudding user response:

You are missing the Configure call:

.ConfigureServices((ctx, services) =>
{
    services.Configure<FTPSettingsOptions>(ctx.Configuration.GetSection(nameof(FTPSettingsOptions)));
    // ...
})

Note that explicit AddOptions call is not needed because Configure performs it internally.

configurationRoot.GetSection(nameof(FTPSettingsOptions)).Bind(options); just binds the data to options instance and does not register anything in the DI (check the docs here or here).

  • Related