.NET Core Worker服务中自定义类Serilog依赖注入后Logger为空的问题排查
问题根源分析
你遇到的问题核心很明确:JsonConvert.DeserializeObject是直接通过反射创建MyCustomProduct实例的,不会经过.NET Core的依赖注入(DI)容器。这意味着你的构造函数里的ILogger<MyCustomProduct>参数不会被DI容器自动填充,自然就会是null,调用日志方法时就会抛出空引用异常。
下面给你几种可行的解决方案,你可以根据自己的需求选择:
方案一:使用Serilog全局静态日志类(最简单)
既然你已经在Program.cs里配置了全局的Log.Logger,完全可以直接在MyCustomProduct里使用Serilog提供的静态Log类,不需要通过构造函数注入。这种方法最简单,不需要改动DI配置或反序列化逻辑。
修改MyCustomProduct类:
public class MyCustomProduct { // 移除构造函数注入的ILogger public void YourMethod() { // 直接使用Serilog的静态Log类 Log.Information("日志内容:从MyCustomProduct输出"); // 如果需要区分上下文,也可以创建一个Logger实例(依然不需要DI) var logger = Log.ForContext<MyCustomProduct>(); logger.Debug("带上下文的调试日志"); } }
方案二:让Json.NET使用DI容器创建实例
如果你坚持要通过构造函数注入ILogger,可以配置Json.NET在反序列化时使用DI容器来解析构造函数参数。步骤如下:
- 在Worker中注入
IServiceProvider:
private readonly ILogger<Worker> _logger; private readonly IServiceProvider _serviceProvider; public Worker(ILogger<Worker> logger, IServiceProvider serviceProvider) { _logger = logger; _serviceProvider = serviceProvider; }
- 将
MyCustomProduct注册到DI容器:
在ConfigureServices方法中添加:
services.AddTransient<MyCustomProduct>();
- 自定义Json.NET的ContractResolver:
这个Resolver会让Json.NET使用DI容器来创建对象实例:
public class DiContractResolver : DefaultContractResolver { private readonly IServiceProvider _serviceProvider; public DiContractResolver(IServiceProvider serviceProvider) { _serviceProvider = serviceProvider; } protected override JsonObjectContract CreateObjectContract(Type objectType) { var contract = base.CreateObjectContract(objectType); // 替换默认的实例创建逻辑,改为从DI容器获取 contract.DefaultCreator = () => _serviceProvider.GetRequiredService(objectType); return contract; } }
- 使用自定义Resolver反序列化:
var settings = new JsonSerializerSettings { ContractResolver = new DiContractResolver(_serviceProvider) }; MyCustomProduct cProduct = JsonConvert.DeserializeObject<MyCustomProduct>(response.Content, settings);
这样反序列化出来的MyCustomProduct实例,其构造函数的ILogger参数就会被DI容器正确注入了。
方案三:反序列化后手动注入Logger
如果不想修改Json.NET的配置,可以在反序列化完成后,手动从DI容器获取Logger并赋值给MyCustomProduct:
- 修改
MyCustomProduct,添加Logger属性:
public class MyCustomProduct { public ILogger<MyCustomProduct> Logger { get; set; } // 添加无参构造函数,让JsonConvert能创建实例 public MyCustomProduct() { } // 或者保留带参构造函数,同时加无参构造函数 public MyCustomProduct(ILogger<MyCustomProduct> logger) { Logger = logger; } public void YourMethod() { Logger?.LogInformation("日志内容:从MyCustomProduct输出"); } }
- 在Worker中手动赋值Logger:
// 先反序列化 MyCustomProduct cProduct = JsonConvert.DeserializeObject<MyCustomProduct>(response.Content); // 从DI容器获取Logger并赋值 cProduct.Logger = _serviceProvider.GetRequiredService<ILogger<MyCustomProduct>>();
方案推荐
- 如果你的日志需求比较简单,方案一是最省心的,不需要改动DI或反序列化逻辑,直接用Serilog的静态类即可。
- 如果你的项目严格遵循依赖注入的设计原则,方案二更符合规范,但需要额外的配置。
- 方案三适合快速临时解决问题,代码改动最小,但不够优雅。
内容的提问来源于stack exchange,提问作者SiStone
相关产品推荐
相关产品推荐

