如何在ASP.NET Core中从自定义NLog渲染器访问作用域服务?
解决思路与方案
你的问题核心是NLog渲染器创建的新作用域和WebAPI请求作用域相互独立,因此无法获取中间件已赋值的ApplicationContext。以下两种方案可以解决这个问题:
方案一:通过HttpContext获取当前请求的作用域服务
注册HttpContextAccessor服务
在Program.cs中添加HttpContext访问器的注册,这是ASP.NET Core获取当前请求上下文的标准方式:builder.Services.AddHttpContextAccessor();修改自定义渲染器的依赖注入
将渲染器的构造函数改为注入IHttpContextAccessor,而非全局的IServiceProvider,这样就能直接拿到当前请求的上下文:public class HelloWorldLayoutRenderer : LayoutRenderer { private readonly IHttpContextAccessor _httpContextAccessor; public HelloWorldLayoutRenderer(IHttpContextAccessor httpContextAccessor) { _httpContextAccessor = httpContextAccessor; } protected override void Append(StringBuilder builder, LogEventInfo logEvent) { builder.Append("hello world!"); var httpContext = _httpContextAccessor.HttpContext; if (httpContext != null) { // 从当前请求的服务容器中直接获取已赋值的ApplicationContext var applicationContext = httpContext.RequestServices.GetService<ApplicationContext>(); builder.Append(applicationContext?.CurrentUserUuid); } } }更新渲染器的实例化逻辑
在注册渲染器时,从全局服务容器中获取IHttpContextAccessor实例传入:ConfigurationItemFactory.Default.CreateInstance = (Type type) => { if (type == typeof(HelloWorldLayoutRenderer)) return new HelloWorldLayoutRenderer(app.Services.GetRequiredService<IHttpContextAccessor>()); else return Activator.CreateInstance(type); };
方案二:将用户信息存入NLog诊断上下文(更轻量)
这种方式无需依赖服务容器,直接将需要的用户信息存入NLog的逻辑诊断上下文,渲染器直接读取即可:
在中间件中存入用户信息
修改中间件代码,将CurrentUserUuid存入NLog的MappedDiagnosticsLogicalContext(MDLC),它会和当前异步请求上下文绑定:public async Task Invoke(HttpContext httpContext, ApplicationContext applicationContext) { string? loggedInUserUuid = httpContext.User.FindFirst(ClaimTypes.NameIdentifier)?.Value; if (loggedInUserUuid != null) { applicationContext.CurrentUserUuid = Guid.Parse(loggedInUserUuid); // 将用户ID存入NLog诊断上下文 MappedDiagnosticsLogicalContext.Set("CurrentUserUuid", applicationContext.CurrentUserUuid); } await _next(httpContext); }修改渲染器直接读取诊断上下文的值
渲染器无需注入任何服务,直接从MDLC中读取值:protected override void Append(StringBuilder builder, LogEventInfo logEvent) { builder.Append("hello world!"); // 从NLog诊断上下文获取用户ID var currentUserUuid = MappedDiagnosticsLogicalContext.Get("CurrentUserUuid"); builder.Append(currentUserUuid?.ToString()); }
注意:如果存在后台任务等非请求上下文的日志场景,方案一中的
HttpContext会为null,需要额外做null判断;方案二中MDLC的值仅在当前请求异步流内有效,非请求场景不会有值。
内容的提问来源于stack exchange,提问作者ValidfroM
相关产品推荐
相关产品推荐

