ASP.NET Core静态字段初始化异常问题排查求助
ASP.NET Core静态字段初始化异常问题分析
问题现象
定义了继承自ActionFilterAttribute的过滤器ConfigChangedAttribute,使用静态字段lastConfig记录初始时间,预期该字段在第一个实例创建前完成初始化,其值应早于首次请求存入Session的时间。但实际校验时发现,首次请求存入Session的myConfigTime竟然小于lastConfig,触发了未预期的UnauthorizedResult。
过滤器代码
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false)] public class ConfigChangedAttribute : ActionFilterAttribute { private static DateTime lastConfig = DateTime.Now; private readonly ILogger<ConfigChangedAttribute> _logger; public ConfigChangedAttribute(ILogger<ConfigChangedAttribute> logger) { _logger = logger; _logger.LogInformation("ConfigChangedAttribute initialized"); } public override void OnActionExecuting(ActionExecutingContext context) { DateTime myConfigTime = context.HttpContext.Session.GetObject<DateTime>("configTime"); if(myConfigTime.Ticks == 0) { _logger.LogInformation(context.ActionDescriptor.DisplayName + " no configtime:" + DateTime.Now.ToString()); context.HttpContext.Session.SetObject("configTime", DateTime.Now); return; } if(myConfigTime < lastConfig) { _logger.LogInformation("myConfigTime:" + myConfigTime.ToString() + " lastConfig:" + lastConfig.ToString()); _logger.LogInformation(context.ActionDescriptor.DisplayName + " configtime changed."); context.HttpContext.Session.SetObject("configTime", DateTime.MinValue); context.Result = new UnauthorizedResult(); } } }
控制器使用方式
[Authorize] [TypeFilter(typeof(ConfigChangedAttribute))] public class HomeController : Controller { // 控制器逻辑 }
日志输出
2023-03-02 15:31:01,259 INFO Application started. Press Ctrl+C to shut down. 2023-03-02 15:31:01,272 INFO Hosting environment: Development 2023-03-02 15:31:01,273 INFO Content root path: D:\algotag\AlgoTag 2023-03-02 15:31:06,056 WARN Sensitive data logging is enabled. Log entries and exception messages may include sensitive application data; this mode should only be enabled during development. 2023-03-02 15:31:07,038 INFO ConfigChangedAttribute initialized 2023-03-02 15:31:07,061 INFO AlgoTag.Controllers.HomeController.Privacy (AlgoTag) no configtime:2023/3/2 15:31:07 2023-03-02 15:31:08,635 INFO ConfigChangedAttribute initialized 2023-03-02 15:31:08,658 INFO myConfigTime:2023/3/2 15:31:07 lastConfig:2023/3/2 15:31:08 2023-03-02 15:31:08,660 INFO AlgoTag.Controllers.HomeController.CompareTag (AlgoTag) configtime changed.
从日志可见:首次请求存入Session的时间是2023/3/2 15:31:07,第二次请求时lastConfig的值却是2023/3/2 15:31:08,晚于Session中的时间,且未对lastConfig做任何修改。
临时解决方案
在构造函数中显式引用lastConfig字段后,问题消失:
public ConfigChangedAttribute(ILogger<ConfigChangedAttribute> logger) { _logger = logger; _logger.LogInformation("ConfigChangedAttribute initialized:" + lastConfig); // 显式引用静态字段 }
补充测试
编写独立测试案例验证静态字段初始化逻辑,结果未复现该问题:
public class Holder { public static int IntVal = 0; public static DateTime DateTimeVal; } public class StaticDateTime { private static DateTime config = DateTime.Now; public StaticDateTime() { } public void configChanged() { DateTime d = Holder.DateTimeVal; if (d.Ticks == 0) { Holder.DateTimeVal = DateTime.Now; return; } if (d < config) { Console.WriteLine("config changed."); } } } class Program { static void Main(string[] args) { StaticDateTime sd = new StaticDateTime(); sd.configChanged(); System.Threading.Thread.Sleep(1500); StaticDateTime sd2 = new StaticDateTime(); sd2.configChanged(); Console.WriteLine("press any key."); Console.ReadKey(); } }
问题原因与正确实现
原因分析
这是JIT延迟初始化导致的:
- .NET中静态字段的初始化并非在程序启动时完成,而是在类第一次被主动引用时触发。
- 原代码中,构造函数和首次请求的
OnActionExecuting逻辑都没有直接引用lastConfig,直到第二次请求进入myConfigTime < lastConfig判断时,才首次触发该静态字段的初始化,此时DateTime.Now的取值自然晚于首次请求存入Session的时间。 - 临时解决方案中,构造函数显式引用了
lastConfig,使得静态字段在第一个实例创建时就完成初始化,其值早于首次请求的Session时间,符合预期逻辑。
正确实现方式
为避免JIT延迟初始化的影响,建议在静态构造函数中显式初始化静态字段,确保其在类首次使用前完成赋值:
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false)] public class ConfigChangedAttribute : ActionFilterAttribute { private static DateTime lastConfig; private readonly ILogger<ConfigChangedAttribute> _logger; // 静态构造函数,确保静态字段在类首次使用前初始化 static ConfigChangedAttribute() { lastConfig = DateTime.Now; } public ConfigChangedAttribute(ILogger<ConfigChangedAttribute> logger) { _logger = logger; _logger.LogInformation("ConfigChangedAttribute initialized"); } // 其余逻辑不变... }
内容的提问来源于stack exchange,提问作者Mike
相关产品推荐
相关产品推荐

