Home > other >  Using DI for Multiple Classes In BackgroundService
Using DI for Multiple Classes In BackgroundService

Time:05-27

I'm building a Folder Monitor Service to show files in multiple folders that have been sitting in the folder for over an hour. I'm having an issue injecting the appsettings.json values in to multiple classes.

appsettings.json file

{
  "FolderLocations": {
    "InsertLocation": "C:\\Insert\\",
    "ReplaceLocation": "C:\\Replace\\",
    "DeleteLocation": "C:\\Delete\\"
  },
  "EmailSettings": {
    "SmtpServer": "host",
    "SmtpPort": 25
  },
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.Hosting.Lifetime": "Information"
    }
  }
}

AppSettingsConfiguration,cs

public class AppSettingsConfiguration
    {
        public string InsertLocation { get; set; }
        public string ReplaceLocation { get; set; }
        public string DeleteLocation { get; set; }
        public string SmtpServer { get; set; }
        public int SmtpPort { get; set; }
    }

Program.cs

IHost host = Host.CreateDefaultBuilder(args)
    .UseWindowsService(options =>
    {
        options.ServiceName = "Folder Monitor";
    })
    .ConfigureServices((hostContext, services) => {
        
        services.Configure<AppSettingsConfiguration>(hostContext.Configuration.GetSection("FolderLocations"));
        services.AddSingleton<IFolderMonitorManager, FolderMonitorManager>();

        services.Configure<AppSettingsConfiguration>(hostContext.Configuration.GetSection("EmailSettings"));
        services.AddLogging();
        services.AddSingleton<IEmail, Email>();
        services.AddHostedService<WindowsBackgroundService>();
    })
    .Build();

await host.RunAsync();

WindowsBackgroundService.cs

public class WindowsBackgroundService : BackgroundService
    {
        private readonly IFolderMonitorManager _folderMonitorManager;
        private readonly ILogger<WindowsBackgroundService> _logger;

        public WindowsBackgroundService(
            IFolderMonitorManager folderMonitorManager,
            ILogger<WindowsBackgroundService> logger) =>
            (_folderMonitorManager, _logger) = (folderMonitorManager, logger);

        protected override async Task ExecuteAsync(CancellationToken stoppingToken)
        {
            while (!stoppingToken.IsCancellationRequested)
            {
                _logger.LogWarning($"===== Starting Folder Monitor @ {DateTime.Now:MM/dd/yy HH:mm:ss} =====");
                await _folderMonitorManager.StartMonitoringProcess();
                
                await Task.Delay(TimeSpan.FromMinutes(1), stoppingToken);
            }
        }
    }

FolderMonitorManager.cs

internal class FolderMonitorManager : IFolderMonitorManager
    {
        private readonly AppSettingsConfiguration _config;
        private readonly ILogger<FolderMonitorManager> _logger;
        private List<string> _folders;
        private List<string> _files;
        private List<string> _filesFoundOverHour;
        private readonly IEmail _email;

        public FolderMonitorManager(IOptions<AppSettingsConfiguration> config, ILogger<FolderMonitorManager> logger)
        {
            _config = config.Value;
            _logger = logger;
            _filesFoundOverHour = new List<string>();
        }
        
        public async Task StartMonitoringProcess()
        {
            try
            {
                await CheckFoldersFromAppSettings();
            }
            catch (Exception)
            {
                throw;
            }
        }

        private async Task CheckFoldersFromAppSettings()
        {
            //File Locations from appsettings.json
            _folders = new(){ 
                _config.InsertLocation,
                _config.DeleteLocation,
                _config.ReplaceLocation
            };
            try
            {
                foreach (var folder in _folders)
                {
                    _files = FolderLocations.GetFilesInLocation(folder);
                    foreach (var file in FolderLocations.GetFileLastWriteTime(_files))
                    {
                        _filesFoundOverHour.Add(file);
                    }
                }
            }
            catch (Exception)
            {    
                throw;
            }
            _logger.LogWarning($"===== Finished Folder Monitor @ {DateTime.Now:MM/dd/yy HH:mm:ss} =====");
            Console.WriteLine(_filesFoundOverHour.Count);
            SendEmail();
        }

        public void SendEmail()
        {
            Console.WriteLine("Email Send hit");
            _email.Send(); //Getting Null Exception here
        }
    }

Email.cs

public class Email : IEmail
    {
        private readonly AppSettingsConfiguration _config;
        private readonly ILogger<Email> _logger;
        private string FromEmailAddress = "[email protected]";

        public Email()
        {
        }

        public Email(IOptions<AppSettingsConfiguration> config, ILogger<Email> logger)
        {
            _config = config.Value;
            _logger = logger;
        }
        public async Task Send()
        {
            // create email message
            var email = new MimeMessage();
            email.From.Add(MailboxAddress.Parse(FromEmailAddress));
            email.To.Add(MailboxAddress.Parse("[email protected]"));
            email.Subject = "Test Email Subject";
            email.Body = new TextPart(TextFormat.Html) { Text = "<h1>Example HTML Message Body</h1>" };

            using (var client = new SmtpClient())
            {
                //Here's where I need to be able to read the appsettings.json
                client.Connect(_config.SmtpServer, _config.SmtpPort, false);

                // Note: only needed if the SMTP server requires authentication
                //client.Authenticate("joey", "password");

                client.Send(email);
                client.Disconnect(true);
            }
        }
    }

In the using statement of Email.Send() is where I need to be able to read the SmtpServer and SmtpPort values.

In the FolderMonitorManager.cs file, I'm getting a Null Exception with _email.Send(). I understand why. Is there a way to be able to read the AppSettingsConfiguration.cs values across all classes?

CodePudding user response:

Since the JSON does not map to the object model then they need to be bound manually when configuring options

IHost host = Host.CreateDefaultBuilder(args)
    .UseWindowsService(options =>
    {
        options.ServiceName = "Folder Monitor";
    })
    .ConfigureServices((hostContext, services) => {
        //Configure settings
        services.Configure<AppSettingsConfiguration>(options => {
            hostContext.Configuration.GetSection("FolderLocations").Bind(options);
            hostContext.Configuration.GetSection("EmailSettings").Bind(options);
        });
        
        services.AddSingleton<IFolderMonitorManager, FolderMonitorManager>();

        services.AddLogging();
        services.AddSingleton<IEmail, Email>();
        services.AddHostedService<WindowsBackgroundService>();
    })
    .Build();
    

Reference: Bind hierarchical configuration

FolderMonitorManager also does not assign a value to _email as it is not being injected into the constructor

public FolderMonitorManager(IOptions<AppSettingsConfiguration> config, ILogger<FolderMonitorManager> logger, IEmail email)
{
    _config = config.Value;
    _logger = logger;
    _filesFoundOverHour = new List<string>();
    _email = email; //<-- 
}
  • Related