【问题标题】:Populate ASP.NET ListBox asynchronously without blocking UI在不阻塞 UI 的情况下异步填充 ASP.NET ListBox
【发布时间】:2014-02-24 14:02:44
【问题描述】:

我在 ASP.NET 用户控件中有一个列表框,我想用从数据库中检索到的可能很大的 List<string> 填充(最多 50k 项)。由于正在填充列表框(在页面加载事件中),我不想阻止 UI。使用同步代码,在列表框填充完成之前不会显示页面:

List<string> items = GetItemsFromDB();    
foreach (var item in items) // can take some time, blocks UI
{
    if (shouldItemBeListed(item))
        ListBox1.Items.Add(item);
}

我来自桌面应用程序背景,实现这一点很简单,例如BackgroundWorker - 用户将看到实时填充新项目的列表框。 Web 的东西似乎有点复杂,我想要一些关于什么是最好的解决方法的建议。

这是我尝试过的:

1.创建后台线程。 效果不太好 - 与桌面 (WinForms) 版本相比,所有内容仍然反应迟钝,并且列表未按预期更新/刷新:

Thread thread = new Thread(new ThreadStart(Populate));
thread.IsBackground = true;
thread.Start(); 

private void Populate()
{
    List<string> items = GetItemsFromDB();    
    foreach (var item in items)
    {
        if (shouldItemBeListed(item))
            ListBox1.Items.Add(item);
    }
}

2。 BackgroundWorker 线程。 比 1 好一点。(至少列表框正在更新新项目),但仍然非常无响应且速度缓慢。

var bw = new BackgroundWorker();
bw.DoWork += (o, args) => Populate();
bw.RunWorkerAsync();

我总是可以将列表框项分成更小的组并根据用户的要求填充更多,但这通常如何正确实施?

【问题讨论】:

  • 我认为您正在寻找的是一个 Ajax 回调,不需要复杂的后台线程。
  • 当你开始有这样的需求时,是时候开始学习 ASP.Net MVC 和/或 ASP.Net Web api了。更容易实现。
  • 再次阅读您的问题,我觉得您做错了。与其加载 50K 项,然后删除不需要的项,不如只加载相关的项?能否将shouldItemBeListed 方法中的逻辑移到数据库查询中?
  • @SteveB 确实可以,而且我会 - 我非常想了解使用“手动”方式(快速压力测试)如何工作。我假设我可以简单地使用 DataSource 属性,但我还不确定这种方法的潜在性能问题。

标签: c# asp.net multithreading .net-3.5 backgroundworker


【解决方案1】:

您不能使用这种方法,因为 asp.net 生命周期(以及一般的网络)。基本上,对网络服务器的每个请求都会为您生成 asp.net 页面类,生成 html,将其发送到客户端,然后关闭实例。使用多线程技术无济于事,因为您将更改控制树AFTER服务器已发送答案。

要解决您的问题,您应该使用&lt;asp:hidden runat='server'&gt; 字段。

然后,使用某种客户端脚本,从 Web 服务调用动态填充下拉列表。

像这样使用 jQuery 的东西(来自内存):

<select id='ddl' onchange='document.getElementById("<%= hidValue.ClientID %>").value=this.value'>
    <option value=''>Loading...</option>
</select>
<asp:Hidden runat='server' id='hidValue' />

$(function(){
   $.ajax("... your api endpoit").then(function(result){
       var items = result.items; // actual implementation will change
       var ddl = $(ddl);
       ddl.html(''); // remove the 'loading' option.
       foreach(var item in items) {
           var option = $("<option>");
           option.attr("value",item.value);
           option.text(item.text);
           ddl.append(option);
       }    
   });
});

不使用&lt;asp:DropDownList&gt; 的原因是您无法在回发之间更改其子项。所以我们使用纯客户端,并在隐藏控件中同步更改,可以在回发时读取。

【讨论】:

    【解决方案2】:

    Damon 说得对,因为您正在寻找 ajax,它应该为您提供您正在寻找的功能。我找到了一个使用 gridview 的示例 frmo MSDN,但它应该是适应性强的:

    <%@ Page Language="C#" %>
    <%@ Import Namespace="System.Collections.Generic" %>
    
    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"     "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
    
    <html xmlns="http://www.w3.org/1999/xhtml" >
    <head runat="server">
    <title>Enter New Employees</title>
    <script runat="server">
        private List<Employee> EmployeeList;
    
        protected void Page_Load()
        {
            if (!IsPostBack)
            {
                EmployeeList = new List<Employee>();
                EmployeeList.Add(new Employee(1, "Jump", "Dan"));
                EmployeeList.Add(new Employee(2, "Kirwan", "Yvette"));
                ViewState["EmployeeList"] = EmployeeList;
            }
            else
                EmployeeList = (List<Employee>)ViewState["EmployeeList"];
    
            EmployeesGridView.DataSource = EmployeeList;
            EmployeesGridView.DataBind();
        }
    
        protected void InsertButton_Click(object sender, EventArgs e)
        {
            if (String.IsNullOrEmpty(FirstNameTextBox.Text) ||
               String.IsNullOrEmpty(LastNameTextBox.Text)) { return; }
    
            int employeeID = EmployeeList[EmployeeList.Count-1].EmployeeID + 1;
    
            string lastName = Server.HtmlEncode(FirstNameTextBox.Text);
            string firstName = Server.HtmlEncode(LastNameTextBox.Text);
    
            FirstNameTextBox.Text = String.Empty;
            LastNameTextBox.Text = String.Empty;
    
            EmployeeList.Add(new Employee(employeeID, lastName, firstName));
            ViewState["EmployeeList"] = EmployeeList;
    
            EmployeesGridView.DataBind();
            EmployeesGridView.PageIndex = EmployeesGridView.PageCount;
        }
    
        protected void CancelButton_Click(object sender, EventArgs e)
        {
            FirstNameTextBox.Text = String.Empty;
            LastNameTextBox.Text = String.Empty;
        }
    
        [Serializable]
        public class Employee
        {
            private int _employeeID;
            private string _lastName;
            private string _firstName;
    
            public int EmployeeID
            {
                get { return _employeeID; }
            }
    
            public string LastName
            {
                get { return _lastName; }
            }
    
            public string FirstName
            {
                get { return _firstName; }
            }
    
            public Employee(int employeeID, string lastName, string firstName)
            {
                _employeeID = employeeID;
                _lastName = lastName;
                _firstName = firstName;
            }
        }
    
    </script>
    </head>
    <body>
    <form id="form1" runat="server">
    <div>
        &nbsp;</div>
        <asp:ScriptManager ID="ScriptManager1" runat="server" EnablePartialRendering="true"      />
        <table>
            <tr>
                <td style="height: 206px" valign="top">
                    <asp:UpdatePanel ID="InsertEmployeeUpdatePanel" runat="server"     UpdateMode="Conditional">
                        <ContentTemplate>
                          <table cellpadding="2" border="0" style="background-    color:#7C6F57">
                            <tr>
                              <td><asp:Label ID="FirstNameLabel" runat="server"     AssociatedControlID="FirstNameTextBox" 
                                             Text="First Name" ForeColor="White" /></td>
                              <td><asp:TextBox runat="server" ID="FirstNameTextBox" /></td>
                            </tr>
                            <tr>
                              <td><asp:Label ID="LastNameLabel" runat="server"     AssociatedControlID="LastNameTextBox" 
                                             Text="Last Name" ForeColor="White" /></td>
                              <td><asp:TextBox runat="server" ID="LastNameTextBox" /></td>
                            </tr>
                            <tr>
                              <td></td>
                              <td>
                                <asp:LinkButton ID="InsertButton" runat="server"     Text="Insert" OnClick="InsertButton_Click" ForeColor="White" />
                                <asp:LinkButton ID="Cancelbutton" runat="server"     Text="Cancel" OnClick="CancelButton_Click" ForeColor="White" />
                              </td>
                            </tr>
                          </table>
                          <asp:Label runat="server" ID="InputTimeLabel"><%=DateTime.Now %>    </asp:Label>
                        </ContentTemplate>
                    </asp:UpdatePanel>
                </td>
                <td style="height: 206px" valign="top">
                    <asp:UpdatePanel ID="EmployeesUpdatePanel" runat="server"     UpdateMode="Conditional">
                        <ContentTemplate>
                            <asp:GridView ID="EmployeesGridView" runat="server"     BackColor="LightGoldenrodYellow" BorderColor="Tan"
                                 BorderWidth="1px" CellPadding="2" ForeColor="Black"     GridLines="None" AutoGenerateColumns="False">
                                <FooterStyle BackColor="Tan" />
                                <SelectedRowStyle BackColor="DarkSlateBlue"     ForeColor="GhostWhite" />
                                <PagerStyle BackColor="PaleGoldenrod"     ForeColor="DarkSlateBlue" HorizontalAlign="Center" />
                                <HeaderStyle BackColor="Tan" Font-Bold="True" />
                                <AlternatingRowStyle BackColor="PaleGoldenrod" />
                                <Columns>
                                    <asp:BoundField DataField="EmployeeID"     HeaderText="Employee ID" />
                                    <asp:BoundField DataField="LastName" HeaderText="Last     Name" />
                                    <asp:BoundField DataField="FirstName" HeaderText="First     Name" />
                                </Columns>
                                <PagerSettings PageButtonCount="5" />
                            </asp:GridView>
                            <asp:Label runat="server" ID="ListTimeLabel"><%=DateTime.Now %>    </asp:Label>
                        </ContentTemplate>
                        <Triggers>
                            <asp:AsyncPostBackTrigger ControlID="InsertButton"     EventName="Click" />
                        </Triggers>
                    </asp:UpdatePanel>
                </td>
            </tr>
        </table>
    </form>
    </body>
    </html>
    

    【讨论】:

    • 网格的初始加载仍然会发生在页面渲染上。异步加载下拉列表无济于事
    猜你喜欢
    • 1970-01-01
    • 2016-02-15
    • 2016-07-04
    • 1970-01-01
    • 2019-10-27
    • 1970-01-01
    • 2021-06-10
    • 2018-12-13
    • 1970-01-01
    相关资源
    最近更新 更多