最近溫習到 ReportViewer,參考網路教學寫 WebForm 顯示 RDLC 報表當伸展操。

【延伸閱讀】

先寫了一個雛型,將整個資料表丟到報表顯示,運作正常:

    public partial class RptViewerLoop : System.Web.UI.Page
    {
        protected void Page_Load(object sender, EventArgs e)
        {
            if (!IsPostBack) 
                SetReportViewer();
        }

        private void SetReportViewer()
        {
            ReportViewer1.ProcessingMode = ProcessingMode.Local;
            ReportViewer1.LocalReport.ReportPath =
                Server.MapPath("~/Reports/PlayerReport.rdlc");
            ReportDataSource ds = DataService.QueryData();
            ReportViewer1.LocalReport.DataSources.Clear();
            ReportViewer1.LocalReport.DataSources.Add(ds);
            //傳入參數在報表顯示查詢範圍
            ReportViewer1.LocalReport.SetParameters(new ReportParameter("Range", "All Data"));
        }
    }

接著我加入查詢條件,在網頁新增欄位 <input type="text" name="rangeSt" >、<input type="text" name="rangeEd" > ,按 <button type="submit"> PostBack 傳回 rangeSt 跟 rangeEd 限定報表查詢區間。另一方面,我希望也能用 QueryString ?rangeSt=...&rangeEd=... 輸入參數,做到 GET/POST 兩吃。於是我移除 Page_Load() 的 if (!IsPostBack) 判斷,並由 Request["rangeSt"]、Request["rangeEd"] 接入 QueryString 或 POST 送回參數當成查詢條件:
(資安提醒:若參數與安全有關,宜寫成 Request.QueryString[paramName] 及 Request.Form[paramName] 明確指定來源,以免被惡意偷渡參數)

    protected void Page_Load(object sender, EventArgs e)
    {
        SetReportViewer(Request["rangeSt"], Request["rangeEd"]);
    }

    private void SetReportViewer(string rangeSt, string ragneEd)
    {
        ReportViewer1.ProcessingMode = ProcessingMode.Local;
        ReportViewer1.LocalReport.ReportPath =
            Server.MapPath("~/Reports/PlayerReport.rdlc");
        ReportDataSource ds = DataService.QueryData(rangeSt, rangeEd);
        ReportViewer1.LocalReport.DataSources.Clear();
        ReportViewer1.LocalReport.DataSources.Add(ds);
        ReportViewer1.LocalReport.SetParameters(new ReportParameter("Range", rangeSt + "~" + rangeEd));
    }

結果悲劇了!

如上圖所示,網頁陷入無窮迴圈,停留在 Loading... 幾秒後閃一下。又出現 Loading...,下方則觀察到每隔兩秒多瀏覽器會使用 XHR 呼叫自己(RptViewerLoop.aspx)一次,周而復始。

爬文發現這是個經典問題,在微軟部落格 Reports Never Stop Loading With VS 2010 有詳細解釋 - ReportViewer 2010 起不使用 IFrame,改用 AsyncRendering/UpdatePanel 產生報表內容,意思是第一次 GET 傳回的 HTML 包含 Script 觸發 AJAX 呼叫自己取得報表內容,由於我拿掉 if (!IsPostBack),故 AJAX 呼叫時也會執行 SetReportViewer(),因為我在其中呼叫了 SetParameters(),會觸發傳回指令要求瀏覽器重新產生報表,於是一切從頭,陷入無窮迴圈。

微軟部落格建議的解法是只有在 GET 時才設定 SetParameters(),我們最早加入的 if (!IsPostBack) 即有此效果,但我的情境需要在 Form POST 時也做查詢,想到兩種解法:

  1. 另外宣告一個 <input name="mode" type="hidden" value="form-post" > 額外參數,用 if (!IsPostBack || Request.Form["mode"] == "form-post") 判斷 GET 及 Form POST。
  2. ReportViewer 透過 ScriptManager 發出 AJAX 呼叫,故可偵測 ScriptManager.IsInAsyncPostBack 避開來自 ReportViewer XHR 的呼叫。

最後,我新增一行判斷,搞定收工。

    protected void Page_Load(object sender, EventArgs e)
    {
        if (ScriptManager1.IsInAsyncPostBack) return;
        SetReportViewer(Request["rangeSt"], Request["rangeEd"]);
    }

Case of infinite loop of Report Viewer during loading and how to avoid it.


Comments

Be the first to post a comment

Post a comment