IStringLocalizer突然失效?线程的“失忆症”

IStringLocalizer服务,配合中间件,根据请求头或Cookie来设置当前线程的语系(Culture)。

问题代码示例:

public class HomeController : Controller
{
    private readonly IStringLocalizer<HomeController> _localizer;
    private readonly ILogger<HomeController> _logger;

    public HomeController(IStringLocalizer<HomeController> localizer, ILogger<HomeController> logger)
    {
        _localizer = localizer;
        _logger = logger;
    }

    public IActionResult Index()
    {
        // 这里一切正常,_localizer 根据请求语系(如en-US)工作
        var message = _localizer["Hello"].Value;
        _logger.LogInformation("主线程消息: {Message}", message); // 输出:Hello

        // 开启一个后台任务
        Task.Run(() =>
        {
            // 这里出问题了!
            var backgroundMessage = _localizer["Hello"].Value;
            _logger.LogInformation("后台任务消息: {Message}", backgroundMessage); // 输出:你好
        });

        return View();
    }
}

[ASP.NET]Core的HttpContext(以及与之关联的语系信息)是与当前执行线程关联的,但它并不随Task的调度而自动流动。

  1. async/await 的“文明人”行为: 在纯粹的async/await代码中,会有一个叫做ExecutionContext的东西在背后默默工作。它像一个贴心的管家,会跟随await前后的代码流动,其中就包含了当前线程的语系(CultureInfo.CurrentCultureCultureInfo.CurrentUICulture)。所以,在大多数异步操作中,语系信息得以保持。
  2. Task.Run 的“野蛮”开局Task.Run用于将一个工作项丢到线程池队列中。线程池线程在开始执行这个任务时,它是一个“干净”的状态。它的语系默认是线程池的语系,而线程池的语系通常就是系统的默认语系(例如zh-CN)。ExecutionContext虽然会流动,但默认情况下,Task.Run不会捕获并流动调用线程的“语系”部分(更准确地说,在.NET Core/.NET 5+中,语系默认不包含在ExecutionContext的流动中,除非应用了兼容性开关)。
解决:在启动新Task之前,手动捕获当前的语系信息,并在任务内部显式地应用它。

警钟:在ASP.NET Core应用中,任何脱离原始HTTP请求上下文的后台操作(如Task.RunBackgroundServiceIHostedService),都必须显式处理语系等上下文信息的传递问题。

posted @ 2025-10-10 13:11  闪存第一搬运工  阅读(11)  评论(0)    收藏  举报