自定义会话状态存储提供程序
此版本程序只是说明 自定义会话 可以放在不同的地方, 可以改造成 把会话分布式存储 等。
一、Web.config
<sessionState mode="Custom" customProvider="JinshuaiCustomSessionStateProvider">
<providers>
<add name="JinshuaiCustomSessionStateProvider" type="SessionStateWebApplication.Sessions.JinshuaiCustomSessionStateProvider" connectionStringName="DefaultConnection" />
</providers>
</sessionState>
二、 JinshuaiCustomSessionStateProvider 提供程序
1 using System; 2 using System.Collections.Generic; 3 using System.Collections.Specialized; 4 using System.IO; 5 using System.Linq; 6 using System.Web; 7 using System.Web.SessionState; 8 9 namespace SessionStateWebApplication.Sessions 10 { 11 public class JinshuaiCustomSessionStateProvider : SessionStateStoreProviderBase 12 { 13 private static Dictionary<string, CustomSession> _dic = new Dictionary<string, CustomSession>(); 14 15 public string AppName 16 { 17 get { return "jinshuaiTest"; } 18 } 19 20 public int TimeOut { get; set; } 21 22 23 public override void Initialize(string name, NameValueCollection config) 24 { 25 TimeOut = 20; 26 27 base.Initialize(name, config); 28 } 29 30 31 public override SessionStateStoreData CreateNewStoreData(HttpContext context, int timeout) 32 { 33 TimeOut = timeout; 34 return new SessionStateStoreData(new SessionStateItemCollection(), SessionStateUtility.GetSessionStaticObjects(context), timeout); 35 } 36 37 public override void CreateUninitializedItem(HttpContext context, string id, int timeout) 38 { 39 var newSession = new CustomSession 40 { 41 SessionId = id, 42 LockDate = DateTime.Now, 43 Locked = false, 44 LockId = 0, 45 SessionItems = string.Empty, 46 Timeout = timeout, 47 Expires = DateTime.Now.AddMinutes(timeout), 48 Created = DateTime.Now, 49 ApplicationName = AppName 50 }; 51 _dic.Add(id, newSession); 52 53 } 54 55 public override void Dispose() 56 { 57 58 } 59 60 public override void EndRequest(HttpContext context) 61 { 62 63 } 64 65 public override SessionStateStoreData GetItem(HttpContext context, string id, out bool locked, out TimeSpan lockAge, out object lockId, out SessionStateActions actions) 66 { 67 68 return GetItemByExclusive(false, context, id, out locked, out lockAge, out lockId, out actions); 69 } 70 71 public override SessionStateStoreData GetItemExclusive(HttpContext context, string id, out bool locked, out TimeSpan lockAge, out object lockId, out SessionStateActions actions) 72 { 73 return GetItemByExclusive(true, context, id, out locked, out lockAge, out lockId, out actions); 74 } 75 76 77 78 public SessionStateStoreData GetItemByExclusive(bool isExclusive, HttpContext context, string id, out bool locked, out TimeSpan lockAge, out object lockId, out SessionStateActions actions) 79 { 80 locked = false; 81 lockAge = TimeSpan.Zero; 82 lockId = null; 83 actions = SessionStateActions.None; 84 85 CustomSession tempSession = null; 86 87 88 if (_dic.ContainsKey(id)) 89 { 90 tempSession = _dic[id]; 91 } 92 else 93 { 94 //没找到相应的会话 95 return null; 96 } 97 98 //如果过期 99 if (tempSession.Expires <= DateTime.Now) 100 { 101 _dic.Remove(id); 102 return null; 103 } 104 105 106 //是否独占访问 107 if (isExclusive) 108 { 109 if (tempSession.Locked) 110 { 111 locked = true; 112 lockAge = (DateTime.Now - tempSession.LockDate); 113 lockId = tempSession.LockId; 114 actions = (SessionStateActions)tempSession.Flags; 115 return null; 116 } 117 } 118 119 return Deserialize(context, tempSession.SessionItems, tempSession.Timeout); 120 121 } 122 123 124 125 126 127 public override void InitializeRequest(HttpContext context) 128 { 129 130 } 131 132 public override void ReleaseItemExclusive(HttpContext context, string id, object lockId) 133 { 134 if (!_dic.ContainsKey(id)) return; 135 136 var tempSession = _dic[id]; 137 tempSession.LockId = (int)(lockId??0); 138 tempSession.Locked = false; 139 tempSession.Expires = DateTime.Now.AddMinutes(tempSession.Timeout); 140 } 141 142 public override void RemoveItem(HttpContext context, string id, object lockId, SessionStateStoreData item) 143 { 144 145 if (!_dic.ContainsKey(id)) return; 146 147 _dic.Remove(id); 148 149 } 150 151 public override void ResetItemTimeout(HttpContext context, string id) 152 { 153 154 if (!_dic.ContainsKey(id)) return; 155 156 var tempSession = _dic[id]; 157 tempSession.Expires = DateTime.Now.AddMinutes(tempSession.Timeout); 158 159 } 160 161 public override void SetAndReleaseItemExclusive(HttpContext context, string id, SessionStateStoreData item, object lockId, bool newItem) 162 { 163 164 string sessItems = Serialize((SessionStateItemCollection)item.Items); 165 166 if (newItem) 167 { 168 var newSession = new CustomSession 169 { 170 SessionId = id, 171 LockDate = DateTime.Now, 172 Locked = false, 173 LockId = 0, 174 SessionItems = sessItems, 175 Timeout = TimeOut, 176 Expires = DateTime.Now.AddMinutes(TimeOut), 177 Created = DateTime.Now, 178 ApplicationName = AppName 179 }; 180 _dic.Add(id, newSession); 181 182 } 183 else 184 { 185 186 if (!_dic.ContainsKey(id)) 187 { 188 var newSession = new CustomSession 189 { 190 SessionId = id, 191 LockDate = DateTime.Now, 192 Locked = false, 193 LockId = 0, 194 SessionItems = sessItems, 195 Timeout = TimeOut, 196 Expires = DateTime.Now.AddMinutes(TimeOut), 197 Created = DateTime.Now, 198 ApplicationName = AppName 199 }; 200 _dic.Add(id, newSession); 201 202 } 203 204 var tempSession = _dic[id]; 205 tempSession.Expires = DateTime.Now.AddMinutes(TimeOut); 206 tempSession.SessionItems = sessItems; 207 tempSession.LockId =(int)(lockId??0); 208 tempSession.LockDate = DateTime.Now; 209 210 } 211 212 213 } 214 215 public override bool SetItemExpireCallback(SessionStateItemExpireCallback expireCallback) 216 { 217 218 return false; 219 } 220 221 222 223 224 225 226 227 private string Serialize(SessionStateItemCollection items) 228 { 229 MemoryStream ms = new MemoryStream(); 230 BinaryWriter writer = new BinaryWriter(ms); 231 232 if (items != null) 233 items.Serialize(writer); 234 235 writer.Close(); 236 237 return Convert.ToBase64String(ms.ToArray()); 238 } 239 240 private SessionStateStoreData Deserialize(HttpContext context, 241 string serializedItems, int timeout) 242 { 243 MemoryStream ms = 244 new MemoryStream(Convert.FromBase64String(serializedItems)); 245 246 SessionStateItemCollection sessionItems = 247 new SessionStateItemCollection(); 248 249 if (ms.Length > 0) 250 { 251 BinaryReader reader = new BinaryReader(ms); 252 sessionItems = SessionStateItemCollection.Deserialize(reader); 253 } 254 255 return new SessionStateStoreData(sessionItems, 256 SessionStateUtility.GetSessionStaticObjects(context), 257 timeout); 258 } 259 260 261 } 262 263 264 [Serializable] 265 public class CustomSession 266 { 267 268 public string SessionId 269 { 270 get; 271 set; 272 } 273 274 public string ApplicationName 275 { 276 get; 277 set; 278 } 279 public DateTime Created 280 { 281 get; 282 set; 283 } 284 public DateTime Expires 285 { 286 get; 287 set; 288 } 289 public DateTime LockDate 290 { 291 get; 292 set; 293 } 294 public int LockId 295 { 296 get; 297 set; 298 } 299 public int Timeout 300 { 301 get; 302 set; 303 } 304 public bool Locked 305 { 306 get; 307 set; 308 } 309 public string SessionItems 310 { 311 get; 312 set; 313 } 314 public int Flags 315 { 316 get; 317 set; 318 } 319 320 321 } 322 }
三 、 使用
protected void Button1_Click(object sender, EventArgs e)
{
Session["jinshuai"] = "你好:" + DateTime.Now.ToString();
}
protected void Button2_Click(object sender, EventArgs e)
{
this.Label1.Text = (Session["jinshuai"]?? "没找到值").ToString();
}
附:
https://msdn.microsoft.com/zh-cn/library/system.web.sessionstate.sessionstatestoreproviderbase(VS.80).aspx
ASP.NET 会话状态使用会话状态存储提供程序将会话数据写入数据存储区,并从中读取会话数据。会话状态存储提供程序是一个类,它继承SessionStateStoreProviderBase 抽象类并使用特定于数据存储区的实现重写其成员。在处理 ASP.NET 页的过程中,SessionStateModule 类会调用会话状态存储提供程序以便与数据存储区进行通信,从而存储和检索会话变量和相关的会话信息,如超时值。
对于每个 SessionID 属性,每个 ASP.NET 应用程序内的会话数据都将单独存储。ASP.NET 应用程序不共享会话数据。
如本主题中的示例所示,可以为 ASP.NET 应用程序指定一个自定义 SessionStateStoreProviderBase 实现,方法是将 sessionState 配置元素的mode 属性设置为 Custom,并将 customProvider 属性设置为自定义提供程序的名称。
锁定会话存储区数据
由于 ASP.NET 应用程序是多线程的,支持对并发请求的响应,因此并发请求可能会尝试访问同一会话信息。考虑下面一种情况,其中一个框架集中的多个框架都访问同一应用程序。框架集内每个框架的单独请求可能会在 Web 服务器中的不同线程上并发执行。如果每个框架源代码的 ASP.NET 页都访问会话状态变量,则可能会出现多个线程并发访问会话存储区。
为避免在会话存储区发生数据冲突以及意外的会话状态行为,SessionStateModule 和 SessionStateStoreProviderBase 类包含了锁定功能,可以在 ASP.NET 页执行期间,以独占方式为特定的会话锁定会话存储项。请注意,即使 EnableSessionState 属性标记为 ReadOnly,同一应用程序中的其他 ASP.NET 页也可以向会话存储区写入,因此对于存储区中只读会话数据的请求可能最终仍将等待锁定数据的释放。
在调用 GetItemExclusive 方法的过程中,在请求开始时就会对会话存储区数据设置锁定。请求完成后,在调用 SetAndReleaseItemExclusive 方法期间释放锁定。
如果 SessionStateModule 对象在调用 GetItemExclusive 或 GetItem 方法期间遇到锁定的会话数据,它将以半秒为间隔重新请求会话数据,直到锁定被释放或者会话数据的锁定时间超过 ExecutionTimeout 属性的值。如果超过了执行超时时间,SessionStateModule 对象将调用ReleaseItemExclusive 方法以释放会话存储区数据并随即请求会话存储区数据。
在针对当前的响应调用 SetAndReleaseItemExclusive 方法前,由于锁定的会话存储区数据可能已经通过调用单独线程上的ReleaseItemExclusive 方法而释放,因此可能会尝试设置和释放已被另一会话释放并修改的会话状态存储区数据。为了避免这种情况,GetItem 和GetItemExclusive 方法返回一个锁定标识符。此锁定标识符必须包含在每个请求中以修改锁定的会话存储区数据。仅当数据存储区中的锁定标识符与 SessionStateModule 提供的锁定标识符匹配时,才能修改会话存储区数据。
删除过期的会话存储区数据
当针对特定的会话调用 Abandon 方法时,将使用 RemoveItem 方法从数据存储区中删除该会话的数据;否则,数据将保留在会话数据存储区供会话的以后请求使用。由 SessionStateStoreProviderBase 实现来删除过期的会话数据。