将 NLog 直接注入 .Net Core 项目总是 NULL?

Posted

技术标签:

【中文标题】将 NLog 直接注入 .Net Core 项目总是 NULL?【英文标题】:Direct Injection of NLog into .Net Core project always NULL? 【发布时间】:2020-11-04 08:55:58 【问题描述】:

我是 .Net Core(使用 3.1)并使用依赖注入的新手。我能够在 Web API 控制器中设置 NLog,但现在我正试图让 NLog 按照我在 API 控制器中所做的基本操作在单独的业务类中工作。我不断收到有关记录器为 NULL 的错误,当我在 _logger 和 _config 上设置断点时,果然它们为 NULL。我不确定我在这里缺少什么。

这是我的商务舱,我认为我的设置正确,但显然没有。

public class ShiftBLL
    
        private static IConfiguration _config;
        private static ILogger<ShiftBLL> _logger;
        
        public ShiftBLL(ILogger<ShiftBLL> logger, IConfiguration config)
        
            _config = config;
            _logger = logger;
        
        

        public static List<AppsShift> GetShifts(string station, string shiftDate)
        
            _logger.LogInformation("Staion: 0 | ShiftDate: 1", station, shiftDate);

             *code removed for clarity. The app breaks on the initial call of _logger.           
        

    

修复

我从 ShiftBLL 类以及本地参数中删除了“静态”。然后我必须在我的控制器中创建一个 ShiftBLL 对象,并从控制器中传入记录器和配置,在控制器中我有 DI 在 ShiftBLL 中工作。我最终在我的控制器中得到了这个:

ShiftBLL BLL = new ShiftBLL(_logger, _config);

listShifts = BLL.GetShifts(station, shiftDate); 

这是我更新的 ShiftBLL:

public class ShiftBLL
    
        private IConfiguration _config;
        private readonly ILogger _logger;

        public ShiftBLL(ILogger logger, IConfiguration config)
        
            _config = config;
            _logger = logger;
        
        

        public List<AppsShift> GetShifts(string station, string shiftDate)
        
            _logger.LogInformation("Staion: 0 | ShiftDate: 1", station, shiftDate);

        

我仍然对依赖注入感到头疼。

【问题讨论】:

您可以尝试从类字段中删除“静态”关键字吗?它们在设计上不应该是静态的。编辑:我刚刚注意到,即使 GetShifts 也是静态的,它是从课堂外调用的吗?你能分享一下 GetiShifts 是如何被调用的代码吗? 所以我从类中删除了静态并创建了一个 ShiftBLL 的对象。看起来这可能奏效了。我本可以发誓我今天早些时候尝试过,它没有用,但看起来知道。这是我在 Controller 中创建的...ShiftBLL BLL = new ShiftBLL(_logger, _config); 像这样声明‘私有只读 ILogger _logger;’ 能否分享一下关于如何注册Nlog的程序代码或startup.cs代码? @BrandoZhang 这是我用的:github.com/NLog/NLog/wiki/Getting-started-with-ASP.NET-Core-3 【参考方案1】:

使用 DI 时,DI 容器会在需要时插入所有依赖项的参数。

我将尝试用一个简化的例子来解释:

public class MyClass

    public MyClass(ShiftBLL shiftBll)
      ..   
 

解析MyClass时,DI容器会做:

    查找创建 MyClass 实例所需的所有依赖项 - 在本例中:ShiftBLL 在此ILoggerIConfiguration 中找到创建ShiftBLL 所需的所有依赖项 查找ILoggerIConfiguration 的注册(因为它们是接口)以及依赖项等。 创建ILoggerIConfiguration 的实例(通过注册),通过将实例注入构造函数来创建ShiftBLL 的实例 创建MyClass 的实例并注入 ShiftBLL 的实例。

所以有了这个链,你所有的构造函数依赖都被创建了。

但是,如果您在ShiftBLL 中使用静态方法,您不确定是否曾调用过 ShiftBLL(带有 ILogger)的构造函数 - 因此 ILogger 字段可能是 null。 .

所以要解决这个问题,你可以这样做:

使方法非静态,或 将所有依赖项(ILogger)作为参数发送到静态方法

【讨论】:

【参考方案2】:

您混淆了静态方法调用和依赖注入。我会说尽可能避免使用静态方法,无论如何,当您使用静态方法时,您无法访问通过依赖注入注入的依赖。例如,您无法从 GetShifts 访问 _logger,因为您没有 ShiftBll 的实例,而且您静态调用它的事实意味着它的构造函数永远不会被调用。

这是一个关于如何设计类及其依赖项以及如何注册它们的工作示例。

public class AController : Controller 
    private readonly ILogger<AController> _logger;
    private readonly ShiftBll _shiftBll;

    public AController(ILogger<AController> logger, ShiftBll shiftBll) 
         _logger = logger;
         _shiftBll = shiftBll;
         //I'm pretty sure you didn't inject the dependency here
         //you preferred to use a static reference, but it is not correct in this case
    
    
    
   public ActionResult AnAction() 
        var shifts = _shiftBll.GetShifts("aStation", "aShiftDate");
   


    public class ShiftBLL
    
        private readonly IConfiguration _config; //not static
        private readonly ILogger<ShiftBLL> _logger; //not static
        
        public ShiftBLL(ILogger<ShiftBLL> logger, IConfiguration config)
        
            _config = config;
            _logger = logger;
        
        

        public List<AppsShift> GetShifts(string station, string shiftDate) //not static
        
            _logger.LogInformation("Staion: 0 | ShiftDate: 1", station, shiftDate);

             *code removed for clarity. The app breaks on the initial call of _logger.           
        

    

.NET Core DI 自动注册控制器,但您必须手动注册自己的服务。为此,请进入 Startup.cs 的 ConfigureServices 方法并添加

  services.AddScoped<ShiftBll>();

现在您可以使用 AController 中的 ShiftBll。

【讨论】:

“当你使用静态方法时,你不能访问通过依赖注入注入的依赖”——这是错误的。 当然你可以访问静态成员。只是 _logger_config 在调用构造函数之前将是“null” :( 但我同意 - 这里没有明显需要“静态”,删除“静态”应该可以解决问题。 @paulsm4 依赖关系可以是暂时的,这意味着消费者的每个实例都将具有不同的依赖关系实例。你将如何使它与静态引用一起工作?实现这一点的唯一方法是通过静态服务定位器,这是 .NET Core 决定不包含在设计中的。此外,服务位置不是依赖注入。我很乐意澄清您对此的任何疑问。 请改写您的回复。在我看来,您是在暗示“静态成员”本质上与 .Net Core 的 DI 不兼容。我不敢相信这是真的。请不要明确地说“尽可能避免使用静态方法”。这只是一个糟糕的建议:(关注这里的具体问题(构造函数从未被调用,因此 DI 从未发生),您正确诊断。 @paulsm4 你能告诉我一个使用从 DI 注入的依赖项的静态方法的工作示例吗?语言或框架无关紧要 我不知道为什么你被标记为基本上是一个“正确”的答案。我不知道为什么 Caverman 没有“接受”它(尽管我很欣赏他的更新——这是一种很好的做法 :))。而且我不知道您为什么不更新您的回复以更多地关注直接问题(“静态”不合适),也许澄清“静态”通常是完全合适的。只是不在这里,现在不是 :( 我特别不同意“尽可能避免使用静态方法”这句话 :(

以上是关于将 NLog 直接注入 .Net Core 项目总是 NULL?的主要内容,如果未能解决你的问题,请参考以下文章

Net Core 2.1 日志记录框架NLog+Mysql配置

.NET Core平台日志记录框架:NLog+Mysql

.net core 杂记:日记记录

.net core使用NLog+Elasticsearch记录日志

ASP.NET Core 使用NLog打印html格式日志

如何在 ASP.NET Core 中使用 NLog 的高级特性