可能一些技術細節涉及到架構實現方案,不過,不影響本意的表達。
跨頁面傳值(按查詢的導出方案)
場景:頁面類型:查詢條件,查詢,導出。
查詢條件比較多,且查詢內容也可能比較多,如果使用URL傳值的話,可能存在URL超長截斷的可能。
原理:
1. 問題:在回調(Ajax或timer)里執行 document.location 或 window.open 方法,瀏覽器會阻止下載(有提示)。要解決,如何把條件 Post 到服務器端,再執行 window.location 方法而不報阻止 。
2. 導出時,先使用 Ajax 把查詢Model 傳遞到服務器,保存在Session 里。
3. 執行 window.location 跳轉。
4. 跳轉頁面檢查Session 里的查詢Model,如果不存在,則進行阻塞等待,等待最大時間 3秒。 如果超時,則拋出錯誤,如果3秒內得到 Ajax 傳遞回來的查詢Model,則繼續執行。
實現方案:
1. 客戶端傳值,先調用 jv.saveSession , 再彈出頁面傳遞參數 : QueryKey。
jv.saveSession("ParkingInfo", $(".divQuery:last", jv.boxdy()).GetDivJson("List_")); jv.PopList({ url: "~/ReportWeb/Parking/ParkingInfo.aspx?QueryKey=ParkingInfo", entity: "Notices" }, null); |
2. 服務器端接收(只能接收一次)
var qModel = LifeSession.OnceGetQuery(Request.QueryString["QueryKey"], new ParkingBiz.ParkingQuery()); if (qModel == null) { Response.Write("接收參數時失敗,請重試,或聯系系統管理員。"); Response.End(); } |
當然 LifeSession

public class LifeSession : IDisposable { List<string> keys = new List<string>(); public object this[string SessionKey] { get { return HttpContext.Current.Session[SessionKey]; } set { GodError.Check(value == null, () => "數據接收異常"); keys.Add(SessionKey); HttpContext.Current.Session[SessionKey] = value; } } public void Dispose() { keys.All(o => { HttpContext.Current.Session.Remove(o); return true; }); } public static T OnceGetQuery<T>(string Key, T NewModel) where T : class { if (Key.HasValue() == false) { throw new GodError("接收Session值的 Key 值不能為空"); } XmlDictionary<string, string> dict = null; //10 sec for (int i = 0; i < 100; i++) { if (HttpContext.Current.Session[Key] != null) { dict = HttpContext.Current.Session[Key] as XmlDictionary<string, string>; if (dict == null) { var err = "數據接收異常 :" + HttpContext.Current.Session[Key].GetType().FullName; LogInfo.To(InfoEnum.Error, MySessionKey.UserName.Get(), err); throw new GodError(err); } HttpContext.Current.Session.Remove(Key); break; } else { Thread.Sleep(100); } } if (dict == null) return null; var retVal = dict.DictionaryToModel(NewModel); GodError.Check(retVal == null, () => "Session項數據 : " + Key + "轉換失敗, 數據:" + dict.ToJson() + ",轉換目標類型:" + typeof(T).FullName); return retVal; } }
js:

jv.saveSession = function (sessionKey, jsonData) { if (!sessionKey) { alert("請輸入 SessionKey "); return false; } if (!jsonData) { alert("請輸入 JsonData "); return false; } $.post(jv.Root + "Master/Home/SaveSession/" + sessionKey + ".aspx", jsonData, function (res) { if (res.msg) { alert(res.msg) }; }); return true; };
執行流程
點擊導出后,其流程圖是這樣的:
1. Client ----------------------------------------------IIS加工並賦Session----> 保存Session
2. Client ----------------------IIS加工並賦Session-----> 取Session
其中: 1和2 到達服務器時間不確定,上圖只說明其中一種特例出錯的情況.
出現的問題
當Session超時后,再執行導出,取不到 Session , 調試發現: 當Session 超時后, 保存 Session 時, Session 的 IsNewSession 是 true .但這不重要. 重要的是,它們是兩個線程,當第2 個線程早到的時候,先執行了Session 的賦值,也就是說已過了IIS賦Session的時間,再也取不到 1號線程賦的Session, 這兩個線程之間不能共享值.
解決方法
因為要清空數據,所以可以換用 全局靜態變量 public static Dictionary<string,StringDict> App {get;set;} 來保存 Session 值。 其Key 是 SessionId 和 原SessionKey 在 OnceGetQuery 時,清除該Key值,即可。
改造后的代碼:
[HttpPost] [ValidateInput(false)] public ActionResult SaveSession(string uid, FormCollection query) { if (uid.HasValue()) { LifeSession.App[Session.SessionID + "_" + uid.Trim()] = query.ToStringDict(); } return new JsonMsg(); }
public class LifeSession : IDisposable { public static Dictionary<string, StringDict> App { get; set; } static LifeSession() { App = new Dictionary<string, StringDict>(); } List<string> keys = new List<string>(); public object this[string SessionKey] { get { return HttpContext.Current.Session[SessionKey]; } set { GodError.Check(value == null, () => "數據接收異常"); keys.Add(SessionKey); HttpContext.Current.Session[SessionKey] = value; } } public void Dispose() { keys.All(o => { HttpContext.Current.Session.Remove(o); return true; }); } public static T OnceGetQuery<T>(string Key, T NewModel) where T : class { if (Key.HasValue() == false) { throw new GodError("接收Session值的 Key 值不能為空"); } XmlDictionary<string, string> dict = null; Key = HttpContext.Current.Session.SessionID + "_" + Key; //10 sec for (int i = 0; i < 100; i++) { if (App.ContainsKey(Key)) { dict = App[Key] as StringDict; if (dict == null) { var err = "數據接收異常 :" + App[Key].GetType().FullName; LogInfo.To(InfoEnum.Error, MySessionKey.UserName.Get(), err); throw new GodError(err); } App.Remove(Key); break; } else { Thread.Sleep(100); } } if (dict == null) return null; var retVal = dict.DictionaryToModel(NewModel); GodError.Check(retVal == null, () => "Session項數據 : " + Key + "轉換失敗, 數據:" + dict.ToJson() + ",轉換目標類型:" + typeof(T).FullName); return retVal; } }
完畢。