我有一个典型的场景,我正在开发 .NET Core 库,并希望调用应用程序能够选择配置日志记录选项。
理想情况下,我想对库中每个类的
ILogger
属性使用依赖注入。我无法解决的是如何让 DI 容器在一个程序集(应用程序可执行文件)中初始化,并在另一个程序集(库程序集)中使用它。
我能想到的最好的例子是下面的示例,其中 DI 容器在执行程序集中初始化,然后存储在库随后访问的“中间”程序集中。我的问题是,是否有一种方法可以不需要 intermediary 程序集,并避免使用可能需要或可能不需要的日志记录实例污染库构造函数? 在下面的设计示例中,每个命名空间代表一个单独的程序集。库程序集对应用程序程序集一无所知。
using System;
using System.Linq;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using Serilog;
namespace MainAssembly
{
class Program
{
public static Microsoft.Extensions.Logging.ILogger logger = NullLogger.Instance;
static void Main()
{
Console.WriteLine("DI + Log Test");
// Set up logging factory.
var loggerFactory = new LoggerFactory();
var serilogConfig = new LoggerConfiguration()
.Enrich.FromLogContext()
.MinimumLevel.Is(Serilog.Events.LogEventLevel.Debug)
.WriteTo.Console(outputTemplate: "{Timestamp:HH:mm:ss.fff} [{Level}] ({SourceContext}.{Method}) {Message}{NewLine}{Exception}")
.CreateLogger();
loggerFactory.AddSerilog(serilogConfig);
// Set up dependency injection services.
ServiceCollection serviceCollection = new ServiceCollection();
serviceCollection.AddSingleton<LoggerFactory>(loggerFactory);
IntermediaryAssembly.DI.ServicesSingleton = serviceCollection.BuildServiceProvider();
// Attempt to get a logger from the DI container.
logger = IntermediaryAssembly.DI.TryGetLogger<Program>();
logger.LogDebug("Logging initialised.");
var lib = new LibraryAssembly.DoIt();
lib.DoWork();
Console.WriteLine("Finished.");
}
}
}
namespace IntermediaryAssembly
{
public class DI
{
public static ServiceProvider ServicesSingleton;
public static ILogger<T> TryGetLogger<T>()
{
if (ServicesSingleton != null && ServicesSingleton.GetServices<LoggerFactory>().Any())
{
return ServicesSingleton.GetRequiredService<LoggerFactory>().CreateLogger<T>();
}
else
{
return NullLogger<T>.Instance;
}
}
}
}
namespace LibraryAssembly
{
public class DoIt
{
public Microsoft.Extensions.Logging.ILogger logger = NullLogger.Instance;
public DoIt()
{
// Attempt to get a logger from the DI container.
logger = IntermediaryAssembly.DI.TryGetLogger<DoIt>();
}
public void DoWork()
{
logger.LogDebug("Doing work...");
}
}
}
使用的套件:
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="3.1.1" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="3.1.7" />
<PackageReference Include="Serilog.Extensions.Logging" Version="3.0.1" />
<PackageReference Include="Serilog.Sinks.Console" Version="3.1.1" />
</ItemGroup>
services.AddLogging();
在您要注入的类中,只需执行基本的构造函数注入,在托管类中输入 ILogger:
private readonly ILogger<DoIt> _logger;
public DoIt(ILogger<DoIt> logger)
{
_logger = logger;
}