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

.NET Framework 4.8自定义Session Provider的GetItem未触发问题

自定义Session Provider中Session[key]未触发GetItem方法的问题排查与解决

问题场景

在.NET Framework 4.8中实现自定义SessionStateStoreProviderBase子类时,调用Session[key]试图读取会话数据,预期会触发Provider中的GetItem方法,但实际没有触发。

相关代码

自定义Session Provider代码

using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Configuration;
using System.IO;
using System.Linq;
using System.Net;
using System.Threading;
using System.Web;
using System.Web.SessionState;

namespace MVC
{
    public class PPMSesseionStateProvider : SessionStateStoreProviderBase
    {
        private string baseURl = string.Empty;

        public override void Initialize(string name, NameValueCollection config)
        {
            if (String.IsNullOrEmpty(config["description"]))
            {
                config.Remove("description");
                config.Add("description", "Custom PPM Session State Store provider");
            }

            baseURl = ConfigurationManager.AppSettings["baseURL"];

            base.Initialize(name, config);
        }

        public override SessionStateStoreData CreateNewStoreData(HttpContext context, int timeout)
        {
            return new SessionStateStoreData(new SessionStateItemCollection(), SessionStateUtility.GetSessionStaticObjects(context), timeout);
        }

        public override void CreateUninitializedItem(HttpContext context, string id, int timeout)
        {}

        public override void Dispose()
        {}

        public override void EndRequest(HttpContext context)
        {}

        public override SessionStateStoreData GetItem(HttpContext context, string id, out bool locked, out TimeSpan lockAge, out object lockId, out SessionStateActions actions)
        {
            //SAME AS GETITEM
            var request = WebRequest.Create($"https://localhost:5000/SessionTest/get/asdfrgfds");
            request.ContentType = "application/json; charset=utf-8";
            request.Method = "GET";
            request.Headers.Add("PPMSessionHeader", "12345");
            var sessionResponse = (HttpWebResponse)request.GetResponse();


            // Initial values for return value and out parameters.
            SessionStateStoreData item = null;
            lockAge = TimeSpan.Zero;
            lockId = null;
            locked = false;
            actions = 0;

            var response = string.Empty; //get the reposnse form the server (look for the object type in the server) and create

            SessionStateItemCollection sessionItems = new SessionStateItemCollection();

            if (sessionResponse.ContentLength > 0)
            {
                var reader = new BinaryReader(sessionResponse.GetResponseStream());
                sessionItems = SessionStateItemCollection.Deserialize(reader);
            }

            return new SessionStateStoreData(sessionItems, SessionStateUtility.GetSessionStaticObjects(context), 1000);
        }

        public override SessionStateStoreData GetItemExclusive(HttpContext context, string id, out bool locked, out TimeSpan lockAge, out object lockId, out SessionStateActions actions)
        {
            //SAME AS GETITEM
            var request = WebRequest.Create($"https://localhost:5000/SessionTest/get/asdfrgfds");
            request.ContentType = "application/json; charset=utf-8";
            request.Method = "GET";
            request.Headers.Add("PPMSessionHeader", "12345");
            var sessionResponse = (HttpWebResponse)request.GetResponse();


            // Initial values for return value and out parameters.
            SessionStateStoreData item = null;
            lockAge = TimeSpan.Zero;
            lockId = null;
            locked = false;
            actions = 0;

            var response = string.Empty; //get the reposnse form the server (look for the object type in the server) and create

            SessionStateItemCollection sessionItems = new SessionStateItemCollection();

            if (sessionResponse.ContentLength > 0)
            {
                var reader = new BinaryReader(sessionResponse.GetResponseStream());
                sessionItems = SessionStateItemCollection.Deserialize(reader);
            }

            return new SessionStateStoreData(sessionItems, SessionStateUtility.GetSessionStaticObjects(context), 1000);
        }

        public override void InitializeRequest(HttpContext context)
        {}

        public override void ReleaseItemExclusive(HttpContext context, string id, object lockId)
        {}

        public override void RemoveItem(HttpContext context, string id, object lockId, SessionStateStoreData item)
        {
           //send request to remove item from session
        }

        public override void ResetItemTimeout(HttpContext context, string id)
        {}

        public override void SetAndReleaseItemExclusive(HttpContext context, string id, SessionStateStoreData item, object lockId, bool newItem)
        {
            //send the key and the value to the API
        }

        public override bool SetItemExpireCallback(SessionStateItemExpireCallback expireCallback) => false;
    }
}

测试控制器Action代码

public ActionResult Index()
{
    string key = "ProductID:1001";
    var customObject = Session[key]; // 此处未触发GetItem方法
    Session.Add(key, "tralalalala");

    return View();
}

问题原因与解决方法

核心原因

ASP.NET Session机制中,GetItem/GetItemExclusive方法只会在会话初始化阶段被调用一次,用于从存储介质加载整个会话数据到内存中的SessionStateItemCollection。后续对Session[key]的读写操作,都是直接操作内存中的集合,不会再触发Provider的GetItem方法。

验证与解决步骤

  1. 确认Provider是否正确注册
    检查Web.config中是否正确配置了自定义Provider,确保没有使用默认的InProc Provider:

    <system.web>
      <sessionState mode="Custom" customProvider="PPMSesseionStateProvider">
        <providers>
          <add name="PPMSesseionStateProvider" type="MVC.PPMSesseionStateProvider" />
        </providers>
      </sessionState>
    </system.web>
    

    注意:如果mode设置为InProc,ASP.NET会忽略自定义Provider,使用内置的内存会话。

  2. 调试会话初始化时机
    在GetItem/GetItemExclusive方法中添加断点,会发现这些方法在第一次访问会话的请求开始时触发,而不是每次访问Session[key]时触发。

  3. 修改Provider逻辑适配机制
    如果需要每次读取Session[key]都从远程API获取数据,不能依赖GetItem方法,需要:

    • 在SessionStateItemCollection中维护本地缓存标记
    • 自定义Session的访问封装类,在读取key时主动调用远程API更新本地集合
    • 或者在InitializeRequest方法中根据需要刷新会话数据(但会影响性能)

    示例封装逻辑:

    public static class SessionHelper
    {
        public static T GetSessionValue<T>(string key)
        {
            var session = HttpContext.Current.Session;
            // 先检查本地缓存,不存在则从API获取
            if (session[key] == null)
            {
                // 调用远程API获取数据
                var value = FetchFromRemoteAPI(key);
                session[key] = value;
            }
            return (T)session[key];
        }
    }
    
  4. 检查会话ID是否有效
    确保请求中包含有效的会话ID(通过Cookie或URL重写传递),如果会话ID无效,ASP.NET会创建新会话,此时会调用CreateNewStoreData而不是GetItem。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.24 11:42:02