簡體   English   中英

通過 IOptions 將 Worker Service 中的依賴注入到控制器以外的類

[英]Dependency Injection in Worker Service to classes other than Controllers by IOptions

我知道這是一個重復的問題,經歷了答案,不知道這里發生了什么。 在這個問題中,我們需要將值從appsettings.json轉移到另一個 class 以外的控制器,這里的ServiceSettings.cs

這是一個類似“hello world”的示例程序,這里我們需要將值從appsettings.json傳輸到插件。

這是文件夾架構
在此處輸入圖像描述

appsettings.json

"Application": {
    "TimerInterval": 10000,
    "LogLevel": "Debug"
  }

我根據 class 庫中的此應用程序設置創建了 class -

應用程序設置.cs

 public class ApplicationSettings
    {
        public int TimerInterval { get; set; }
        public string LogLevel { get; set; }
    }

我嘗試通過最后一行代碼從 appsettings 推送數據

services.Configure<ApplicationSettings>(hostContext.Configuration.GetSection("Application")); 

程序.cs

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

        public static IHostBuilder CreateHostBuilder(string[] args) =>
            Host.CreateDefaultBuilder(args)
           .UseWindowsService()
                .ConfigureServices((hostContext, services) =>
                {
                    services.AddSingleton<IConfiguration>(hostContext.Configuration);
// Service Settings Injected here
                    services.AddOptions<ServiceSettings>();
                    services.AddHostedService<Worker>();

                    services.Configure<ApplicationSettings>(hostContext.Configuration.GetSection("Application")); 
// for configure application
                   
                });
    }

在工作人員 class 的啟動方法期間,我需要從 ServiceSettings() 獲取值,該值始終返回null值。

Worker.cs(重新編輯)

 public class Worker : BackgroundService
    {
        private readonly ILogger<Worker> _logger;
        private readonly IConfiguration _configuration;
        private ServiceSettings _settings;

        public Worker(ILogger<Worker> logger, IConfiguration config)
        {
            _logger = logger;
            _configuration = config;            
        }

        public override Task StartAsync(CancellationToken cancellationToken)
        {
            
            Console.WriteLine("Start Asynch Method");

            // Load Settings From Configuration Files
            _settings = new ServiceSettings();
            _settings.Load();

            _logger.LogInformation("Settings: {setting}", _settings.TimerInterval);
            return base.StartAsync(cancellationToken);
        }
        
        protected override async Task ExecuteAsync(CancellationToken stoppingToken)
        {
           var values = _configuration.GetSection("DataSources").Get<List<DataSource>>();           
            while (!stoppingToken.IsCancellationRequested) {                
              await Task.Delay(Convert.ToInt32(_configuration["Application:TimerInterval"]), stoppingToken);
            }
        }
    }

下面提供的服務設置值接收 null 值

服務設置.cs

    public class ServiceSettings 
    {
        private readonly IOptions<ApplicationSettings> _appSettings;
        public ServiceSettings(IOptions<ApplicationSettings> appSettings)
        {
            _appSettings = appSettings;
        }
        
        public int TimerInterval { get; set; }

        public string LogLevel { get; set; }

        public void Load()
        {
            // Error is shown here
            try { TimerInterval = Convert.ToInt32(_appSettings.Value.TimerInterval); }
            catch { TimerInterval = 60; }

            try 
            // Here too
            { LogLevel = Convert.ToString(_appSettings.Value.LogLevel).ToLower(); }
            catch { LogLevel = "info"; }
        }
    }

我對工人服務很陌生,我在這里想念什么? 請用資源指導我謝謝大家。

這似乎是一個設計問題。

首先讓我們修復組合根。 避免注入IConfiguration 可以將其視為代碼異味,因為IConfiguration理想情況下應在啟動時使用。

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

    public static IHostBuilder CreateHostBuilder(string[] args) =>
        Host.CreateDefaultBuilder(args)
            .UseWindowsService()
            .ConfigureServices((hostContext, services) => {
                IConfiguration config = hostContext.Configuration;
                
                // parse settings
                ApplicationSettings appSettings = config
                    .GetSection("Application").Get<ApplicationSettings>();
                
                //set defaults.
                if(appSettings.TimerInterval == 0)
                    appSettings.TimerInterval = 60;
                
                if(string.IsNullOrWhiteSpace(appSettings.LogLevel))
                    appSettings.LogLevel = "Debug";
                
                services.AddSingleton(appSettings); //<-- register settings run-time data
                services.AddHostedService<Worker>();
            });
}

請注意如何從配置中提取設置並將其添加到服務集合中。 由於已經有一個強定義的類型( ApplicationSettings ),根據原始問題中顯示的內容,確實不需要ServiceSettings

更新工作人員以明確依賴所需的實際 object。

public class Worker : BackgroundService {
    private readonly ILogger<Worker> _logger;
    private readonly ApplicationSettings settings;

    public Worker(ILogger<Worker> logger, ApplicationSettings settings) {
        _logger = logger;
        this.settings = settings; //<-- settings injected.
    }

    public override Task StartAsync(CancellationToken cancellationToken) {
        Console.WriteLine("Start Asynch Method");
        _logger.LogInformation("Settings: {setting}", settings.TimerInterval);
        return base.StartAsync(cancellationToken);
    }
    
    protected override async Task ExecuteAsync(CancellationToken stoppingToken) {
        while (!stoppingToken.IsCancellationRequested) {
          await Task.Delay(settings.TimerInterval), stoppingToken);
        }
    }
}

您總是必須從您的服務集合中獲取實例。 您通常通過將它們注入 class 構造函數來執行此操作。

 // WRONG
 // Worker.cs
 _settings = new ServiceSettings();
  1. 此代碼無法編譯,因為您的 ServiceSettings class 有一個需要一個參數但沒有給出參數的構造函數。
  2. 您的 class 應該如何在沒有任何參考的情況下了解存儲在服務集合中的選項?

那么讓兩個類的數據相同, ServiceSettingsApplicationSettings都一樣,這似乎是沒有意義的。 如果您需要服務注入中的應用程序設置IOptions<ApplicationSettings>就是這樣。 如果您需要單獨的設置類,請將它們作為IOption<MyOtherSectionSettings>提供。

最后,它可能看起來像這樣:

 public class Worker {
   private readonly ApplicationSettings _settings;
   private readonly ILogger<Worker> _logger;
   
   public Worker(IOptions<ApplicationSettings> settingsAccessor, ILogger<Worker> logger) {
     _settings = settingsAccessor.Value;
     _logger = logger;
   }

   public override Task StartAsync(CancellationToken cancellationToken) {
        
        Console.WriteLine("Start Asynch Method");
        _logger.LogInformation("Settings: {setting}", _settings.TimerInterval);
        return base.StartAsync(cancellationToken);
   }
 }

請注意,讀取 settingsAccessor.Value 是框架真正嘗試訪問配置的地方,因此在這里我們應該考慮錯誤條件(如果之前未驗證)。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM