2

【茶包射手日記】ReportViewer 陷入無窮迴圈

 2 years ago
source link: https://blog.darkthread.net/blog/report-viewer-infinite-loop/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
【茶包射手日記】ReportViewer 陷入無窮迴圈-黑暗執行緒

最近溫習到 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"]);
    }

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK