You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.29 18:05:01