在 BackgroundService 中对多个 类 使用 DI

Using DI for Multiple Classes In BackgroundService

我正在构建一个文件夹监控服务来显示多个文件夹中的文件,这些文件已经在文件夹中放置了一个多小时。我在将 appsettings.json 值注入多个 类.

时遇到问题

appsettings.json 文件

{
  "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 = "NOREPLY@domain.com";

        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("user@domain.com"));
            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);
            }
        }
    }

Email.Send()using 语句中,我需要能够读取 SmtpServerSmtpPort 值。

FolderMonitorManager.cs 文件中,我得到 Null Exception_email.Send()。我明白为什么。有没有办法能够读取所有 类 中的 AppSettingsConfiguration.cs 值?

由于JSON没有映射到对象模型,所以需要在配置选项时手动绑定

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();
    

参考:Bind hierarchical configuration

FolderMonitorManager 也不会为 _email 赋值,因为它没有被注入构造函数

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