我浏览了许多教程并观看了几个有关在 .NET Core 控制台应用程序中使用 DI 的视频。然而,它们似乎都没有达到我想要的效果。在大型控制台应用程序中,我们可能会调用/引用多个类,并且每个类都可能需要访问记录器或配置设置。我遇到的所有教程和解释都只显示了一层深度,并将日志记录和配置 DI 实例传递到第一层。
如果类 1 调用类 2,然后类 2 调用类 3,会怎样。假设类 3 需要获取配置值,或者类 2 需要将一些信息或调试数据写入日志。如何使用 2 类和 3 类中的 DI 访问 ILogger 和 IConfiguration?
我根据 Tim Corey 的视频教程“.NET Core Console App with Dependency Injection, Logging, and Settings”创建了一个非常简单的示例,他在其中为控制台应用程序设置了配置和 Serilog。但同样,他只深入到最初的工人阶级。
该应用程序由 3 个主要类组成,Program.cs(用于设置配置)、Runner.cs(工作服务类)和 SubClass.cs(从 Runner.cs 调用的子类)。我的代码适用于记录和检索辅助服务类 (Runner.cs) 中的设置,但是当我尝试从子类访问记录器或配置设置时,我遇到了问题。
程序.cs
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Serilog;
namespace DiConsoleExample
{
internal class Program
{
static void Main(string[] args)
{
var builder = new ConfigurationBuilder();
BuildConfig(builder);
Log.Logger = new LoggerConfiguration()
.ReadFrom.Configuration(builder.Build())
.Enrich.FromLogContext()
.WriteTo.Console()
.CreateLogger();
Log.Logger.Information("Application Starting");
var host = Host.CreateDefaultBuilder()
.ConfigureServices((context, services) =>
{
services.AddTransient<IRunner, Runner>();
})
.UseSerilog()
.Build();
var svc = ActivatorUtilities.CreateInstance<Runner>(host.Services);
svc.Run();
}
static void BuildConfig(IConfigurationBuilder builder)
{
builder.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
.AddJsonFile($"appsettings.{Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT")}.json", optional: true, reloadOnChange: true)
.AddEnvironmentVariables();
}
}
}
Runner.cs
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
namespace DiConsoleExample
{
public interface IRunner
{
void Run();
}
public class Runner : IRunner
{
private readonly ILogger<Runner> _log;
private readonly IConfiguration _config;
public Runner(ILogger<Runner> log, IConfiguration config)
{
_log = log;
_config = config;
}
public void Run()
{
_log.LogInformation("Value for 'MyRunnerSetting': {settingValue}", _config.GetValue<string>("MyRunnerSetting"));
var sub = new SubClass();
sub.GetMySetting();
}
}
}
子类.cs
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
namespace DiConsoleExample
{
public class SubClass
{
private readonly ILogger<Runner> _log;
private readonly IConfiguration _config;
public SubClass(ILogger<Runner> log, IConfiguration config)
{
_log = log;
_config = config;
}
public void GetMySetting()
{
_log.LogInformation("Value for 'MySubClassSetting': {settingValue}", _config.GetValue<string>("MySubClassSetting"));
}
}
}
appsettings.json
{
"MyRunnerSetting": "Runner123",
"MySubClassSetting": "SubClass234"
}
我遇到的错误是,当我创建一个新的 SubClass() 时,它无法使用 DI 注入的日志和配置服务:
There is no argument given that corresponds to the required parameter 'log' of 'SubClass.SubClass(ILogger<Runner>, IConfiguration)
同样,如果 Runner.cs 中没有对 SubClass 进行调用,则效果很好,但我需要知道如何通过 DI 访问 Logger 和 Configurations。我知道我可以在每个类中放置每个类的构建/配置语句,但这似乎违背了 DI 的目的并重复了代码。
哦,我在 VS 2022 中使用 .NET 8。
编辑。我不应该在我的解释中使用“子类”,因为它不继承任何东西。我应该使用“NestedClass”(我的错)
我的问题是,由于我的 Runner.cs 可能会在大型应用程序中新建多个嵌套类,因此如何将 ILogger 和 IConfiguration 放入这些嵌套类中?
在 .net 框架中,我可以将 log4net 放入任何嵌套类中以开始日志记录,或在任何类中调用 ConfigurationManager 来获取设置。在 Core ASP.NET 中,我可以在每个控制器中使用带有 ILogger 和 IConfiguration 的构造函数,但我无法弄清楚如何在 Runner.cs 服务之外的任何嵌套类中访问或引用 ILogger 和 IConfiguration。
扔掉您所了解的有关 .NET Framework 的有关日志记录和配置的所有内容,因为它们与 .NET (Core) 世界无关。您无需手动
new
进行分类,DI 框架会为您提供。并且您不直接绑定到 IConfiguration
,而是使用 选项模式。所以你的课程看起来像这样:
public record SubClassOptions(
string MySubClassSetting);
public class Runner
{
private ILogger<Runner> _logger;
private ISubClass _subClass;
public Runner(ILogger<Runner> logger, ISubClass subClass)
{
_logger = logger;
_subClass = subClass;
}
public void Run()
{
_subClass.GetMySetting();
}
}
public interface ISubClass
{
void GetMySetting();
}
public class SubClass : ISubClass
{
private readonly ILogger<Runner> _logger;
private readonly SubClassOptions _options;
public SubClass(ILogger<Runner> log, SubClassOptions config)
{
_log = log;
_config = config;
}
public void GetMySetting()
{
_log.LogInformation(
"Value for 'MySubClassSetting': {SettingValue}",
_config.MySubClassSetting);
}
}
以及您的服务注册:
services.AddSingleton<SubClassOptions>(
configuration.GetRequiredSection("SubClassSection").Get<SubClassOptions>());
services.AddScoped<ISubClass, SubClass>();
services.AddScoped<Runner>();