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

SQL Server会话状态下手动实例化控制器访问Session出现空引用异常

问题根本原因

这个空引用问题和你切换到SQL Server存储Session的模式没有直接关系,换存储只是让之前藏着的代码问题暴露出来而已,核心问题出在手动实例化跨控制器调用的写法上:

  • 控制器类里能直接访问的Session属性,是从基类Controller继承来的,它不是独立存在的全局对象,本质是附着在当前请求的HttpContext下面的子属性。
  • 正常的控制器实例,是ASP.NET请求管道在处理HTTP请求的过程中自动创建的,创建完成后会自动把当前请求的HttpContext(包括关联的Session、登录用户身份、路由解析结果等)绑定到控制器实例上,所以你在所属控制器的正常方法里读Session一点问题都没有。
  • 你手动new SalesInvoiceSettingController()创建的对象,完全没有经过请求管道的初始化流程,它内部的HttpContext是空的,连带下面挂的Session自然也是null,直接调用.ToString()就必然抛“未将对象引用设置到对象的实例”的错误。
    之前用InProc模式的时候没出问题,只是因为你从来没在这个手动创建的空壳控制器里真正读过Session,并非这种写法本身是正确的。
可落地的解决方法
  • 最优先的修正方向:彻底废弃手动跨控制器实例化调用的写法
    控制器的定位是接收、响应HTTP请求的入口,不是给你当通用业务类随便实例化调用的,这是MVC/WebAPI框架下的典型反模式,长期这么写,早晚会遇到模型状态丢失、权限校验失效、路由解析错误等一堆奇奇怪怪的上下文问题。
  • 最优的重构方案:把跨控制器复用的逻辑抽成独立的业务服务
    把两个控制器都要用到的发票设置保存、读取当前登录用户Session的公共逻辑,抽成独立的服务类,注册到项目的依赖注入容器里,两个控制器都从容器中拿到同一个服务实例来使用,从根源上避免手动创建空壳对象的问题。
    参考实现代码:
    // 独立公共服务,通过内置的IHttpContextAccessor获取当前请求上下文
    public class InvoiceSettingService
    {
        private readonly IHttpContextAccessor _httpContextAccessor;
        public InvoiceSettingService(IHttpContextAccessor httpContextAccessor)
        {
            _httpContextAccessor = httpContextAccessor;
        }
        // 统一读取当前登录用户名的逻辑,所有调用方共用
        public string GetCurrentUserName()
        {
            return _httpContextAccessor.HttpContext?.Session?.GetString("UserName");
        }
        public string SaveInvoiceSettings()
        {
            var currentUser = GetCurrentUserName();
            // 把原SaveInvoiceSettings方法里的所有业务逻辑迁移到这里即可
        }
    }
    
    控制器层的代码调整为通过构造函数注入服务,不再手动创建其他控制器对象:
    public class CompanyController : Controller
    {
        private readonly InvoiceSettingService _invoiceSettingService;
        // 构造函数注入已经初始化好的服务实例
        public CompanyController(InvoiceSettingService invoiceSettingService)
        {
            _invoiceSettingService = invoiceSettingService;
        }
        public string AddNewCompany()
        {
            // 直接调用服务方法,无需手动new其他控制器
            _invoiceSettingService.SaveInvoiceSettings();
        }
    }
    
  • 临时过渡的兜底方案(不推荐长期使用)
    如果短期内没有条件完成代码重构,可以在手动创建控制器实例后,手动把当前请求的上下文绑定到新实例上,让它能正常访问到关联的Session对象:
    public String AddNewCompany()
    {
        SalesInvoiceSettingController invoiceSetting = new SalesInvoiceSettingController();
        // 手动给新实例绑定当前请求的上下文
        invoiceSetting.ControllerContext = new ControllerContext
        {
            HttpContext = this.HttpContext
        };
        // 绑定完成后再调用方法,即可正常读取Session值
        invoiceSetting.SaveInvoiceSettings();
    }
    
    必须明确的是,这种写法只是临时绕开了空引用的报错,因为它没有完整初始化控制器的所有绑定属性(比如模型校验状态、路由匹配信息、用户权限上下文等),后续依然可能触发其他未知的运行时异常,只能作为重构前的临时过渡手段。

内容的提问来源于stack exchange,提问作者Haris Tasawar

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.26 09:15:03