You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C#中如何为未注册DI的类获取ILogger<TCategoryName>实例

问题背景

假设有一个未通过AddSingleton等方式注册到依赖注入(DI)框架的Person类:

public class Person
{
    public Person(string name, int age);
    ...
}

若要为该类获取ILogger<Person>实例,目前尝试了两种方案但均存在问题:

方案1:在类内直接创建日志实例

public class Person
{
    private ILogger<Person> logger =
        LoggerFactory.Create(builder => builder
            .AddConsole()
            .SetMinimumLevel(LogLevel.Information)
            .AddFilter("Microsoft", LogLevel.Warning)
            ...
        );

    public Person(string name, int age);
    ...
}

问题:每个类都需重复配置日志,且日志级别无法通过appsettings.json控制。

方案2:从已注册DI的外部服务传入日志实例

public class SchoolService: ISchoolService
{
    // 用于SchoolService内部日志
    private ILogger<SchoolService> _logger;
    // 用于Person内部日志
    private ILogger<Person> _personLogger;

    public SchoolService(ILogger<SchoolService> logger, ILogger<Person> personLogger)
    {
        _logger = logger;
        _personLogger = personLogger;
    }

    public void DoSomething()
    {
        _logger.LogInformation("Creating student");
        Person student = new Person("Ben", 15, _personLogger);
        ...
    }
}

问题:层级调用时需传递大量不同日志实例,且不想为此修改类的构造函数或方法。

请问是否存在更优雅的方式为未注册DI的类创建日志实例?


解决方案

方法1:注入ILoggerFactory动态生成日志实例

在已注册DI的服务(比如SchoolService)中注入ILoggerFactory,而非预先注入所有可能用到的ILogger<T>。当需要创建Person时,用工厂直接生成ILogger<Person>再传递——如果允许修改Person构造函数,这是最简洁的方式:

public class SchoolService: ISchoolService
{
    private readonly ILogger<SchoolService> _logger;
    private readonly ILoggerFactory _loggerFactory;

    public SchoolService(ILogger<SchoolService> logger, ILoggerFactory loggerFactory)
    {
        _logger = logger;
        _loggerFactory = loggerFactory;
    }

    public void DoSomething()
    {
        _logger.LogInformation("Creating student");
        var personLogger = _loggerFactory.CreateLogger<Person>();
        Person student = new Person("Ben", 15, personLogger);
        ...
    }
}

如果不想修改Person构造函数,可以给Person加个静态方法初始化日志:

public class Person
{
    private static ILogger<Person> _logger;
    public static void SetLogger(ILogger<Person> logger) => _logger = logger;

    public Person(string name, int age)
    {
        _logger?.LogInformation("创建Person实例:{Name}, {Age}", name, age);
    }
}

// 在应用启动时从DI获取并设置
var personLogger = serviceProvider.GetRequiredService<ILogger<Person>>();
Person.SetLogger(personLogger);

方法2:静态日志访问器(无构造函数修改需求)

创建一个静态访问器类,在应用启动时用DI容器初始化,后续未注册DI的类直接通过它获取日志实例:

public static class LoggerAccessor
{
    private static ILoggerFactory _loggerFactory;

    public static void Initialize(ILoggerFactory loggerFactory)
    {
        _loggerFactory = loggerFactory;
    }

    public static ILogger<T> GetLogger<T>()
    {
        if (_loggerFactory == null)
            throw new InvalidOperationException("LoggerAccessor未初始化");
        return _loggerFactory.CreateLogger<T>();
    }
}

// 在Program.cs中初始化
var builder = WebApplication.CreateBuilder(args);
// ... 其他服务注册
var app = builder.Build();
LoggerAccessor.Initialize(app.Services.GetRequiredService<ILoggerFactory>());
app.Run();

// Person类中直接使用
public class Person
{
    private readonly ILogger<Person> _logger = LoggerAccessor.GetLogger<Person>();

    public Person(string name, int age)
    {
        _logger.LogInformation("创建Person实例:{Name}, {Age}", name, age);
    }
}

这种方式复用了DI中配置好的日志规则(比如从appsettings.json读取级别),既不用改构造函数,也不用层层传递日志实例。

方法3:将Person注册到DI(推荐)

如果没有特殊限制,最符合DI设计理念的方式是把Person注册到容器,用ActivatorUtilities处理混合参数(DI注入+自定义参数):

// Program.cs中注册Person
builder.Services.AddTransient<Person>();

// 修改Person构造函数注入日志
public class Person
{
    private readonly ILogger<Person> _logger;

    public Person(string name, int age, ILogger<Person> logger)
    {
        _logger = logger;
    }
}

// SchoolService中创建Person实例
public class SchoolService: ISchoolService
{
    private readonly ILogger<SchoolService> _logger;
    private readonly IServiceProvider _serviceProvider;

    public SchoolService(ILogger<SchoolService> logger, IServiceProvider serviceProvider)
    {
        _logger = logger;
        _serviceProvider = serviceProvider;
    }

    public void DoSomething()
    {
        _logger.LogInformation("Creating student");
        // 自动注入ILogger<Person>,同时传入name和age参数
        Person student = ActivatorUtilities.CreateInstance<Person>(_serviceProvider, "Ben", 15);
        ...
    }
}

内容的提问来源于stack exchange,提问作者kzfid

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.22 13:17:41