C# 通过IOOptions将辅助服务中的依赖项注入到控制器以外的类

C# 通过IOOptions将辅助服务中的依赖项注入到控制器以外的类,c#,dependency-injection,asp.net-core-3.1,C#,Dependency Injection,Asp.net Core 3.1,我知道这是一个重复的问题,我看了一遍答案,不知道这里发生了什么。在这个问题中,我们需要将值从appsettings.json传输到另一个类,而不是这里的ControllersServiceSettings.cs 这是一个类似“hello world”的示例程序,这里我们需要将值从appsettings.json传输到插件 这是文件夹体系结构 appsettings.json "Application": { "TimerInterval": 10

我知道这是一个重复的问题,我看了一遍答案,不知道这里发生了什么。在这个问题中,我们需要将值从
appsettings.json
传输到另一个类,而不是这里的ControllersServiceSettings.cs

这是一个类似“hello world”的示例程序,这里我们需要将值从
appsettings.json
传输到插件

这是文件夹体系结构

appsettings.json

"Application": {
    "TimerInterval": 10000,
    "LogLevel": "Debug"
  }
我在类库中基于此应用程序设置创建了一个类-

ApplicationSettings.cs

 public class ApplicationSettings
    {
        public int TimerInterval { get; set; }
        public string LogLevel { get; set; }
    }
 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
                   
                });
    }
    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"; }
        }
    }
我尝试通过最后一行代码从appsettings推送数据

services.Configure<ApplicationSettings>(hostContext.Configuration.GetSection("Application")); 
下面提供了接收空值的服务设置值

ServiceSettings.cs

 public class ApplicationSettings
    {
        public int TimerInterval { get; set; }
        public string LogLevel { get; set; }
    }
 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
                   
                });
    }
    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"; }
        }
    }
公共类服务设置
{
私有只读IOPS\u应用设置;
公共服务设置(IOPS应用设置)
{
_appSettings=appSettings;
}
公共int TimerInterval{get;set;}
公共字符串日志级别{get;set;}
公共空荷载()
{
//错误显示在这里
请尝试{TimerInterval=Convert.ToInt32(_appSettings.Value.TimerInterval);}
catch{TimerInterval=60;}
尝试
//这里也是
{LogLevel=Convert.ToString(_appSettings.Value.LogLevel).ToLower();}
catch{LogLevel=“info”;}
}
}

我对工人服务很陌生,我在这里错过了什么?请为我提供资源,谢谢大家。

这似乎是一个设计问题

首先,让我们修复组合根。避免注入
i配置
。它可以被看作是一种代码气味,因为
i配置
在理想情况下应用于启动

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

这似乎是一个设计问题

首先,让我们修复组合根。避免注入
i配置
。它可以被看作是一种代码气味,因为
i配置
在理想情况下应用于启动

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

您始终必须从服务集合中获取实例。通常通过在类构造函数中注入它们来实现这一点

 // WRONG
 // Worker.cs
 _settings = new ServiceSettings();
  • 此代码不会编译,因为ServiceSettings类的构造函数需要一个参数,但没有给出任何参数
  • 在没有任何引用的情况下,您的类如何知道服务集合中存储的选项
  • 那么,让两个类具有相同的数据似乎毫无意义
    ServiceSettings
    ApplicationSettings
    是相同的。如果您需要服务注入中的应用程序设置
    IOptions
    ,仅此而已。如果需要单独的设置类,请将它们作为
    IOption
    提供

    最后,它可能看起来是这样的:

     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);
       }
     }
    
    公共类工作者{
    专用只读应用程序设置\u设置;
    专用只读ILogger\u记录器;
    公共工作者(IOptions设置访问器、ILogger记录器){
    _设置=设置Accessor.Value;
    _记录器=记录器;
    }
    公共覆盖任务StartAsync(CancellationToken CancellationToken){
    WriteLine(“启动异步方法”);
    _logger.LogInformation(“设置:{setting}”,_Settings.TimerInterval);
    返回base.StartAsync(cancellationToken);
    }
    }
    

    请注意,reading settingsAccessor.Value是框架真正尝试访问配置的地方,因此这里我们应该考虑错误情况(如果之前未验证)。

    您必须始终从服务集合中获取实例。通常通过在类构造函数中注入它们来实现这一点

     // WRONG
     // Worker.cs
     _settings = new ServiceSettings();
    
  • 此代码不会编译,因为ServiceSettings类的构造函数需要一个参数,但没有给出任何参数
  • 在没有任何引用的情况下,您的类如何知道服务集合中存储的选项
  • 那么,让两个类具有相同的数据似乎毫无意义
    ServiceSettings
    ApplicationSettings
    是相同的。如果您需要服务注入中的应用程序设置
    IOptions
    ,仅此而已。如果需要单独的设置类,请将它们作为
    IOption
    提供

    最后,它可能看起来是这样的:

     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);
       }
     }
    
    公共类工作者{
    专用只读应用程序设置\u设置;
    专用只读ILogger\u记录器;
    公共工作者(IOptions设置访问器、ILogger记录器){
    _设置=设置Accessor.Value;
    _记录器=记录器;
    }
    公共覆盖任务StartAsync(CancellationToken CancellationToken){
    WriteLine(“启动异步方法”);
    _logger.LogInformation(“设置:{setting}”,_Settings.TimerInterval);
    返回base.StartAsync(cancellationToken);
    }
    }
    

    请注意,reading settingsAccessor.Value是框架真正尝试访问配置的地方,因此在这里我们应该考虑错误条件(如果之前未验证)。

    您介意检查一下吗?你介意也检查一下吗?你介意也检查一下吗?你介意也检查一下吗?