.NET Framework 4.8自定义Session Provider的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方法。
验证与解决步骤
确认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,使用内置的内存会话。调试会话初始化时机
在GetItem/GetItemExclusive方法中添加断点,会发现这些方法在第一次访问会话的请求开始时触发,而不是每次访问Session[key]时触发。修改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]; } }- 在
检查会话ID是否有效
确保请求中包含有效的会话ID(通过Cookie或URL重写传递),如果会话ID无效,ASP.NET会创建新会话,此时会调用CreateNewStoreData而不是GetItem。
内容的提问来源于stack exchange,提问作者Wasyster

