65.9K
CodeProject 正在变化。 阅读更多。
Home

改进 ASP.Net 中冗长的 Page_Loads

starIconstarIconstarIconstarIconstarIcon

5.00/5 (3投票s)

2016年2月12日

CPOL

5分钟阅读

viewsIcon

16510

使用 UpdatePanel 和异步数据加载替换缓慢的 Page_Load 函数

引言

最近又发生在我身上了。

我的一位同事给我看了一个遗留的 ASP.Net 应用程序,并想知道如何修复其中的一个棘手问题。他们的网页开始显示,但下拉菜单会出现——半绘制,一团糟——并在那里冻结 6-7 秒,直到页面突然自行整理好,并正确显示。

原因?

开发人员决定将他们所有耗时的加载 SQL Server 数据代码放入他们的 Page_Load 函数中。一旦开始运行,浏览器就会停滞不前,直到数据加载完成。

在本文中,我将通过将耗时代码移至单独的线程,然后获取网页以便在数据准备好后自行更新,为您介绍解决此问题的简单步骤。

免责声明

毋庸置疑,使用 Angular 等现代技术加载和显示数据要高效得多,也更易于维护,正如我在我的其他文章中所述,但本文是一篇简单的分步指南,可快速改进遗留页面,而无需重写所有内容。

遵循这些步骤,只需很少的努力,您就可以使缓慢、痛苦的网页更加响应,并在下次年度评估时获得加薪。也许。

遗留代码

让我们从代码以前的样子开始。

基本上,我们的开发人员将从 SQL Server 加载数据的代码放在 Page_Load 函数中。    

我们将通过创建 10 秒的延迟来模拟此问题,然后生成一个示例 DataTable ,并将 DataGrid 设置为使用 DataTable 作为其数据源。

protected void Page_Load(object sender, EventArgs e)
{
    if (IsPostBack)
        return;

    //  Wait for 10 seconds...
    System.Threading.Thread.Sleep(10000);

    //  .. then create a DataTable containing some sample data...
    System.Data.DataTable dt = new System.Data.DataTable("Drivers");

    dt.Columns.Add("UserID", Type.GetType("System.Int64"));
    dt.Columns.Add("Surname", Type.GetType("System.String"));
    dt.Columns.Add("Forename", Type.GetType("System.String"));
    dt.Columns.Add("Sex", Type.GetType("System.String"));
    dt.Columns.Add("Date of Birth", Type.GetType("System.DateTime"));

    dt.Rows.Add(new object[] { 1, "James", "Spencer", "M", new DateTime(1962, 3, 19) });
    dt.Rows.Add(new object[] { 2, "Edward", "Jones", "M", new DateTime(1939, 7, 12) });
    dt.Rows.Add(new object[] { 3, "Janet", "Spender", "F", new DateTime(1996, 1, 7) });
    dt.Rows.Add(new object[] { 4, "Maria", "Percy", "F", null });
    dt.Rows.Add(new object[] { 5, "Malcolm", "Marvelous", "M", new DateTime(1973, 5, 7) });

    //  ...and bind it to our ASP.Net GridView control.
    this.grid.DataSource = dt;
    this.grid.DataBind();
}

这段代码的结果是网页显示速度很慢。通常浏览器在显示页面时会进行到一半,然后冻结几秒钟,最后才设法显示完整的网页。

与此同时,用户一直盯着他们的浏览器,想知道它是否崩溃了,或者他们的笔记本电脑是否卡住了。

这不是愉快的体验。

让我们改为异步!

好的,让我们做得更好。

第一步是进入您的 .aspx 文件,并找到您网页中包含将在耗时任务完成时更新的控件的部分。

在我们的例子中,这很简单,只有我们的 DataGrid 在数据加载完成后需要更新。

<asp:DataGrid ID="grid" runat="server"></asp:DataGrid>

我们需要做的是将此控件(或一组控件)包装在 UpdatePanel ContentTemplate 中,并且我们需要在页面中添加一个 Timer 控件。

<asp:UpdatePanel ID="panel" runat="server" UpdateMode="Conditional">
    <ContentTemplate>
        <asp:Timer ID="MyTimer" OnTick="timer_tick" Interval="1000" runat="server" />



        <!--  Put your controls that you will need updating, here.. -->
        <asp:DataGrid ID="grid" runat="server"></asp:DataGrid>



    </ContentTemplate>
</asp:UpdatePanel>

我们的目标是尽快将网页显示到屏幕上,一旦数据加载完成,我们就可以返回并更新(仅)网页的这一部分。

下一步是将数据加载代码从 Page_Load 函数中分离出来,放入自己的函数中,并使其填充一个变量(在本例中为 DataTable ),该变量存储在 Session 变量中。  

您将需要这些 Session 变量之一来加载您将要异步加载的每个数据块。

我们还需要一个布尔 Session 变量 bReadyToDisplayData ,当我们的数据加载完成后,我们会将其设置为“true”。

System.Data.DataTable dt
{
    get
    {
        return (System.Data.DataTable)Session["table1"];
    }
    set
    {
        Session["table1"] = value;
    }
}
bool bReadyToDisplayData
{
    get
    {
        return (bool)Session["bReadyToDisplayData"];
    }
    set
    {
        Session["bReadyToDisplayData"] = value;
    }
}

protected void Page_Load(object sender, EventArgs e)
{
    if (IsPostBack)
        return;

   bReadyToDisplayData = false;

   LoadDataFromWebService();
}

private void LoadDataFromWebService()
{
    //  (This would be our time-consuming function which loads data from some web service.)

    //  Wait for 10 seconds...
    System.Threading.Thread.Sleep(10000);

    //  .. then create a DataTable containing some sample data...
    dt = new System.Data.DataTable("Drivers");

    //  ... etc ...


    //  Note: We no longer bind our DataTable to our control in this function.


    //  Once all of our data has been loaded, we set this boolean variable, which will
    //  trigger our DataGrid controls to be displayed, showing our freshly-loaded data.
    bReadyToDisplayData = true;
}

这样看起来更好,但我们仍然同步调用我们的数据加载函数,所以接下来,让我们更改 Page_Load 以异步调用它。

System.Threading.Thread thread = new System.Threading.Thread(LoadDataFromWebService);
thread.Start();

最后只需要添加一件事来将所有这些联系起来。

您会注意到我们已经在 .aspx 文件中添加了一个 Timer 控件。这是为了让我们能够定期检查数据是否已加载完成,并在加载完成后填充我们的网格控件。

为此,我们需要一个 Timer “tick”处理程序。

(没有这个 Timer 变量,我们就没有更新 UpdatePanel 的方法。)

protected void timer_tick(object sender, EventArgs e)
{
    //  Every second, our webpage will call this function.
    //  If our background thread to load some JSON data has finished running, then 
    //  we'll want to display the data in a grid, and can then stop the timer.
    //
    if (bReadyToDisplayData == false)
    {
        return;     //  Our background thread is still running.
    }


    //  Our JSON data has finished loading !


    //  Populate our Grid with the JSON data
    this.grid.DataSource = dt;
    this.grid.DataBind();

    //  We can now update our UpdatePanel, and stop the timer.  Our webpage is now complete!
    this.panel.Update();                
    MyTimer.Enabled = false;
}

就是这样!

现在,当您打开这个 aspx 网页时,它将非常快速地显示,然后开始加载数据,并在完成后,我们将 DataGrid DataSource 设置为指向该数据,并让它显示它。

Session变量

关于使用 Session 变量的快速说明。  

如果您发现此代码对您不起作用,请检查您的 .aspx 页面是否能够保存 Session 变量值。  要做到这一点,请在您的 Page_Load 函数中设置 bReadyToDisplayData 变量后设置一个断点。    

        protected void Page_Load(object sender, EventArgs e)
        {
            if (IsPostBack)
                return;

            bReadyToDisplayData = false;

            //  Put a breakpoint on the following line...
            System.Threading.Thread thread = new System.Threading.Thread(LoadSomeData);

如果您随后检查 Session 变量,其“Count”值应至少为 1 (因为我们刚刚添加了一个 Session 变量来存储 bReadyToDisplayData 变量)。

如果此 Count 值为 0,则表示您的网页未存储 Session 变量,此代码将无法正常工作。

两件事需要检查

1.您的服务器名称是否包含下划线字符?Apparently, this can cause problems with IIS.

2.尝试将以下几行添加到您的 Global.asax 文件中

    protected void Session_Load(object sender, EventArgs e)
    {
        Session["info"] = 1;
    }

此 Session 变量问题似乎主要在 Internet Explorer 11 上被注意到。

摘要

好了,就是这样。这是一个简单的 walkthrough,演示了如何将一些非常糟糕的代码变得更用户友好。

我见过许多内部网页出现此问题,尤其是在处理大量数据时。用户通常会单击一个链接来打开网页,在等待旋转的沙漏 30 秒后,会放弃并转向其他地方。

现在,我们可以轻松地修改这些代码,使其更具响应性。

当然,您可以进一步扩展,例如在 UpdatePanel 中添加一个“请稍候”消息,并在数据加载完成后将其隐藏。

就我个人而言,当我知道我的网页需要几秒钟来准备所需数据时,我喜欢在屏幕上显示一个计时器。当用户能够实际看到一个计时器计数时,他们倾向于更信任网页。这比一个看起来“卡住”的浏览器窗口要友好得多。

© . All rights reserved.