IStringLocalizer突然失效?线程的“失忆症”
IStringLocalizer
问题代码示例:
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的调度而自动流动。
async/await的“文明人”行为: 在纯粹的async/await代码中,会有一个叫做ExecutionContext的东西在背后默默工作。它像一个贴心的管家,会跟随await前后的代码流动,其中就包含了当前线程的语系(CultureInfo.CurrentCulture和CultureInfo.CurrentUICulture)。所以,在大多数异步操作中,语系信息得以保持。Task.Run的“野蛮”开局:Task.Run用于将一个工作项丢到线程池队列中。线程池线程在开始执行这个任务时,它是一个“干净”的状态。它的语系默认是线程池的语系,而线程池的语系通常就是系统的默认语系(例如zh-CN)。ExecutionContext虽然会流动,但默认情况下,Task.Run不会捕获并流动调用线程的“语系”部分(更准确地说,在.NET Core/.NET 5+中,语系默认不包含在ExecutionContext的流动中,除非应用了兼容性开关)。
解决:在启动新Task之前,手动捕获当前的语系信息,并在任务内部显式地应用它。
警钟:在ASP.NET Core应用中,任何脱离原始HTTP请求上下文的后台操作(如Task.Run、BackgroundService、IHostedService),都必须显式处理语系等上下文信息的传递问题。

浙公网安备 33010602011771号