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

如何在ASP.NET Core中通过DI将IEmailSender注入自定义日志记录器?

解决自定义日志记录器中注入IEmailSender的问题

你的核心问题是错误地把服务实例(IEmailSender)放到了配置类里,而且日志提供者和日志器的构造函数没有正确接收依赖注入的服务。下面是一步步的修正方案:

1. 调整日志配置类

配置类只应该存放纯配置参数,不要放服务实例,所以把IEmailSender从EmailLoggerConfiguration中移除:

public sealed class EmailLoggerConfiguration
{
    public int EventId { get; set; }
    public string EmailToSendTo { get; set; }
    // 移除IEmailSender字段
}

2. 修改日志提供者(EmailLoggingProvider)

在提供者的构造函数中直接注入IEmailSender,这样创建日志器时可以把它传递下去:

internal class EmailLoggingProvider : ILoggerProvider
{
    private readonly IDisposable? _onChangeToken;
    private EmailLoggerConfiguration _currentConfig;
    private readonly ConcurrentDictionary<string, EmailLogger> _loggers =
        new(StringComparer.OrdinalIgnoreCase);
    // 注入IEmailSender
    private readonly IEmailSender _emailSender;

    public EmailLoggingProvider(
        IOptionsMonitor<EmailLoggerConfiguration> config,
        IEmailSender emailSender) // 添加这个参数
    {
        _currentConfig = config.CurrentValue;
        _onChangeToken = config.OnChange(updatedConfig => _currentConfig = updatedConfig);
        _emailSender = emailSender;
    }

    public ILogger CreateLogger(string categoryName) =>
        // 创建EmailLogger时传入_emailSender
        _loggers.GetOrAdd(categoryName, name => new EmailLogger(name, GetCurrentConfig, _emailSender));

    private EmailLoggerConfiguration GetCurrentConfig() => _currentConfig;

    public void Dispose()
    {
        _loggers.Clear();
        _onChangeToken?.Dispose();
    }
}

3. 修正日志器(EmailLogger)

更新构造函数接收IEmailSender,并在Log方法中使用:

internal class EmailLogger : ILogger
{
    private readonly string _categoryName;
    private readonly Func<EmailLoggerConfiguration> _getCurrentConfig;
    private readonly IEmailSender _emailSender;

    // 添加IEmailSender参数
    public EmailLogger(string categoryName, Func<EmailLoggerConfiguration> getCurrentConfig, IEmailSender emailSender)
    {
        _categoryName = categoryName;
        _getCurrentConfig = getCurrentConfig;
        _emailSender = emailSender;
    }

    public IDisposable? BeginScope<TState>(TState state) where TState : notnull => default!;

    public bool IsEnabled(LogLevel logLevel) => !string.IsNullOrEmpty(_getCurrentConfig().EmailToSendTo);

    public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter)
    {
        var config = _getCurrentConfig();
        if (string.IsNullOrEmpty(config.EmailToSendTo) || exception == null)
            return;

        // 注意:Log是同步方法,异步邮件发送需要同步等待完成
        _emailSender.SendEmailAsync(config.EmailToSendTo, "Admin exception", exception.ToString())
            .GetAwaiter()
            .GetResult();
    }
}

4. 注册依赖并配置日志

在ConfigureServices中,先注册IEmailSender的实现,再配置EmailLogger:

public void ConfigureServices(IServiceCollection services)
{
    // 1. 先注册IEmailSender的实现(比如你的AuthMessageSender)
    services.AddSingleton<IEmailSender, AuthMessageSender>(sp => 
        new AuthMessageSender(/* 这里传入你的构造参数,比如从Configuration里取 */));

    // 2. 配置日志
    services.AddLogging(loggingBuilder =>
    {
        var loggingSection = Configuration.GetSection("Logging");
        loggingBuilder.AddFile(loggingSection);
        loggingBuilder.AddConsole();
        loggingBuilder.AddDebug();

        // 配置EmailLogger
        loggingBuilder.AddEmailLogger(c =>
        {
            c.EmailToSendTo = Configuration["Logging:Email:EmailToSendTo"];
            c.EventId = 100; // 根据需要设置
        });
    });

    // 其他服务注册...
}

关键注意点

  • 不要把服务实例放到配置类中:配置类的作用是存储可配置的参数,服务应该通过依赖注入获取。
  • 处理异步方法的同步调用:日志的Log方法是同步的,所以调用异步的SendEmailAsync时需要用GetAwaiter().GetResult()来等待完成(如果担心阻塞,可以考虑用后台任务队列,但简单场景下同步等待足够)。
  • 服务生命周期匹配:EmailLoggingProvider是注册为Singleton的,所以注入的IEmailSender最好也是Singleton生命周期,避免生命周期不匹配的问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.10 03:31:36