【问题标题】:how to optimize a "foreach" event from a gridview in asp.net webform?如何从asp.net webform中的gridview优化“foreach”事件?
【发布时间】:2016-10-08 17:35:01
【问题描述】:

我有一个Event,它在 c#.net 中更改 TextBox 文本时开始。 Event 遍历 GridView 行。 GridView 是使用数据库中的信息创建的。这个GridView 有八个TemplateFields,七个带有TextBox,一个带有DropDownList 控件。

问题是这个事件需要 26 到 27 秒。

Foreach 行,它应该:

  • 检查 [4] 列内容是否等于“BATCH”,如果为真,则绘制 使用不同颜色的整行。
  • 从 [0] 列中提取 ID。
  • 使用此 ID 和来自 TextBox 的日期进行查询 如果该记录已存在于数据库中。
  • 如果记录存在,则应从TemplateField 将其打印到TextBoxDropDownList
  • 如果数据库中的某些记录为空,则有几条 TextBox 不应该打印它。

附加信息:

  • GridView 是使用数据库中的数据创建的,使用 QUERYSqlDataSource 内。这发生在同一事件中。这 QUERY 包含一些INNER JOIN,数据没有存储在 相同的数据库表:

    SELECT Dealer.IDDealer, Batch.IDBatch, Lpars.Nombre, Dealer.DealerCodigo, Batch.Nombre AS Expr1, Batch.CTStart AS Expr2 FROM Lpars INNER JOIN Dealer ON Lpars.IDLpar = Dealer.IDLpar INNER JOIN Batch ON Dealer.IDDealer = Batch.IDDealer INNER JOIN [1Monday] ON Batch.IDBatch = [1Monday].IDBatch WHERE (Batch.Status = 'Enabled') ORDER BY Batch.CTStart

  • 响应时间问题不是因为顶部的SQL QUERY。我尝试过这个 单独和此查询的 SQL Server 响应时间更短 超过 2 秒。

  • 我从数据库中提取了一些DATETIME 数据。所以,之前 我打印成TextBox,我需要更改DATETIME格式 我的需要。这就是我将提取的数据存储到变量中的原因 在打印之前。

这是我的代码:

-

 //WHEN THE TEXT FROM TEXTBOX CHANGES:
        protected void TextDate_TextChanged(object sender, EventArgs e)
        {
            //THE GRIDVIEW IS CREATED:
            GridView1.DataSourceID = DatosLunes.ID;
            GridView1.DataBind();

            //A) I CREATE VARIABLES TO CHARGE THE DATA THAT CAMES FROM THE DATABASE WHEN PROCEED WITH THE QUERY
            string VarsDateGV;
            string VarsStartGV;
            string VarsScchkGV;
            string VarsEndGV;
            string VarsDurationGV;
            string VarsBeforeGV;
            string VarsAfterGV;

            //B) FOREACH ROW, THE PROCESS START TO:
            foreach (GridViewRow row in GridView1.Rows)
            {
                //B.1) IDENTIFY EACH CONTROL INTO ROW COLUMNS:
                TextBox DateGV = row.FindControl("DateGV") as TextBox;
                TextBox StartGV = row.FindControl("StartGV") as TextBox;
                TextBox ScchkGV = row.FindControl("ScchkGV") as TextBox;
                TextBox EndGV = row.FindControl("EndGV") as TextBox;
                TextBox DurationGV = row.FindControl("DurationGV") as TextBox;
                HiddenField DedicatedGV = row.FindControl("DedicatedGV") as HiddenField;
                HiddenField NotDedicatedGV = row.FindControl("NotDedicatedGV") as HiddenField;
                DropDownList DropDownGV = row.FindControl("DropDownGV") as DropDownList;
                TextBox BeforeGV = row.FindControl("BeforeGV") as TextBox;
                TextBox AfterGV = row.FindControl("AfterGV") as TextBox;
                DateTime FechaCT1 = DateTime.Parse(TextDate.Text, CultureInfo.InvariantCulture);

                //B.2) IF THE [4] COLUMN STRING IS EQUAL TO "BATCH", THE ROW IS PAINTED
                if (row.RowType == DataControlRowType.DataRow)
                {
                    string NombreBatch = row.Cells[4].Text;

                    if (NombreBatch == "BATCH")
                    {
                        row.BackColor = System.Drawing.Color.NavajoWhite;
                    }
                }

                //B.3) THE QUERY STARTS
                if (row.RowType == DataControlRowType.DataRow)
                {
                    // B.3.1) EXTRACTS THE ROW ID FROM [0] COLUMN
                    string IDBatch = row.Cells[0].Text;

                    //B.3.2) USE A DATATABLE TO CHARGE DATA FROM THE QUERY "TRAEFILAHO"
                    CADCATOPS.DSCATOPS.BatchDatos1DataTable Fila = CADCATOPS.CADBatchHandoff.TraeFilaHO(Convert.ToInt32(IDBatch), Convert.ToString(FechaCT1));

                    //B.3.3) FOREACH ROW IN THE DATATABLE, THE DB INFORMATION IS SAVED INTO THE VARIABLES CREATED BEFORE (IN THE "A" ITEM).
                    foreach (DataRow row1 in Fila.Rows)
                    {
                        VarsDateGV = row1["FechaBatch"].ToString();
                        VarsStartGV = row1["Inicio"].ToString();
                        VarsScchkGV = row1["FinDedicado"].ToString();
                        VarsEndGV = row1["FinNoDedicado"].ToString();
                        VarsDurationGV = row1["DuracionBatch"].ToString();
                        DropDownGV.Text = row1["Estado"].ToString();
                        VarsBeforeGV = row1["DuracionBefore"].ToString();
                        VarsAfterGV = row1["DuracionAfter"].ToString();

                        /********* FROM NOW ON:
                         B.3.3.1) I VALIDATE IF THE DATETIME DATA EXTRACTED FROM THE DB EXISTS FOR A FEW ITEMS. IF EXISTS, THE FORMAT IS CHANGED FOR MY NEEDS, AND PRINTED.

                         MAYBE YOU ARE ASKING WHY I VALIDATE IT FOR SEPARATED AND NOT ALL TOGETHER, THIS IS BECAUSE I NEED TO CHECK IT SEPARATELLY. 
                         IF "THIS" DATA DOESN'T EXISTS, DON'T BRING IT TO THE GRIDVIEW, BUT IF "THIS OTHER" DATA EXISTS, I NEED TO SHOW IT.
                         *********/
                        if (VarsDateGV != "")
                        {
                            DateTime VardDateGV = DateTime.Parse(VarsDateGV, CultureInfo.InvariantCulture);
                            DateTime VardStartGV = DateTime.Parse(VarsStartGV);
                            DateGV.Text = VardDateGV.ToString("MM/dd/yyyy");
                            StartGV.Text = VardStartGV.ToString("HH:mm");
                        }

                        if (VarsEndGV != "")
                        {
                            DateTime VardEndGV = DateTime.Parse(VarsEndGV);
                            DateTime VardDurationGV = DateTime.Parse(VarsDurationGV);
                            EndGV.Text = VardEndGV.ToString("HH:mm");
                            DurationGV.Text = VardDurationGV.ToString("HH:mm");
                        }

                        if (VarsScchkGV != "")
                        {
                            DateTime VardScchkGV = DateTime.Parse(VarsScchkGV);
                            ScchkGV.Text = VardScchkGV.ToString("HH:mm");
                        }

                        if (VarsBeforeGV != "")
                        {
                            DateTime VardBeforeGV = DateTime.Parse(VarsBeforeGV);
                            BeforeGV.Text = VardBeforeGV.ToString("HH:mm");
                        }

                        if (VarsAfterGV != "")
                        {
                            DateTime VardAfterGV = DateTime.Parse(VarsAfterGV);
                            AfterGV.Text = VardAfterGV.ToString("HH:mm");
                        }
                    }
                }
            } //FOREACH LOOP IS COMPLETED.
        }

你有什么建议来优化这个活动吗?

更新:ConnorsFan 帮助我发现问题(谢谢)。

问题在于查询,因为它运行了 50 次(或 GridView 长度)。我试图避免它,响应时间不到 4 秒。问题是我需要它来处理查询。有没有办法为它优化代码?

【问题讨论】:

  • GridView1.Rows 有多少行,Filea.Rows 有多少行?
  • GridView1 有 50 行。 Fila 有 1 行。
  • 查询是否执行了50次?查询的 50 次调用的响应时间是 2 秒吗?
  • 嗨康纳斯范。查询执行 50 次,每行执行一次。现在我试图避免该查询,并且响应要好得多(4秒)。既然你帮我找到了原因,那我该如何优化呢?
  • 是的 - 您执行循环并创建一个包含您将发送到查询的所有可能参数的表参数,然后调用查询以获取一个数据集。然后,您可以为每个数据子集过滤该数据集。一般来说,您应该尽可能不频繁地调用您的数据库,并在一次调用中获取您将需要的所有数据。迭代的数据库调用总是会带来非常糟糕的性能。

标签: c# asp.net sql-server gridview


【解决方案1】:

那么总的迭代次数大约是 100 次迭代。这根本不需要很长时间。

您能否在事件处理程序的开头和结尾使用 Stopwatch 类并获取函数执行的总毫秒数 - 目前似乎没有任何理由期望代码变慢。我们需要将代码的执行速度与刷新 UI 的时间分开。

或者按照How to suspend a DataGridView while updating its columns 的建议在执行更改期间暂停 GridView 绘制和重新格式化,然后在完成后恢复布局。

【讨论】:

    【解决方案2】:

    我假设TextDate 在 GridView 之外。据我了解,您的内部查询返回一条记录。您可以修改它以返回循环内需要的所有记录(包括IDBatch 字段),并在进入循环之前运行它。在循环中,您会找到带有主键的特定记录(如 here 所示)。

    protected void TextDate_TextChanged(object sender, EventArgs e)
    {
        ...
    
        CADCATOPS.DSCATOPS.BatchDatos1DataTable AllFila = CADCATOPS.CADBatchHandoff.AllTraeFilaHO(Convert.ToString(FechaCT1));
        AllFila.PrimaryKey = new DataColumn[] { AllFila.Columns["IDBatch"] };
    
        foreach (GridViewRow row in GridView1.Rows)
        {
            ....
            if (row.RowType == DataControlRowType.DataRow)
            {
                string IDBatch = row.Cells[0].Text;
                DataRow foundRow = AllFila.Rows.Find(IDBatch);
                ...
            }
        }
    }
    

    如果查询返回的 DataTable 中的记录与 GridView 中的排序方式相同,则可以加快速度。行索引将匹配,您无需在 DataTable 中查找行。

    更新

    如果可以直接从填充 GridView 的数据源中获取 TextBox 数据,则可以消除 foreach 循环(以及所有相关处理)。可以使用Eval的第二个参数来格式化数据:

    <asp:TextBox ID="DateGV" runat="server" Text='<%# Eval("FechaBatch", "{0:MM/dd/yyyy}") %>' ... />
    <asp:TextBox ID="StartGV" runat="server" Text='<%# Eval("Inicio", "{0:HH:mm}") %>' ... />
    

    【讨论】:

    • GridView 已经在 foreach 之外(在它之前)创建,但在同一事件中,使用另一个查询。这工作正常,而且速度很快。问题在 foreach 内部。我将更新我的代码以比这里更好地显示它。
    • 我认为CADCATOPS.CADBatchHandoff.TraeFilaHO 是需要时间的调用。你可以把它移到循环之外。这是我的建议。我不知道它是否适合你。
    • 另一种方法是在填充 GridView 的查询中考虑日期(如果可能的话)。您不需要任何额外的处理。
    • 我更新了答案以显示我在之前评论中提到的替代方法。
    • ConnorsFan,我需要在循环中使用它,因为它会检查带有 ID 和日期的行,如果记录存在,它会显示在同一行中带有文本框的列中。这个过程应该用 gridview 长度一遍又一遍地开始。我曾尝试使用另一种方法从数据库中获取数据(使用 datareader),但它所花费的时间与其他方法完全相同。
    猜你喜欢
    • 2017-02-18
    • 1970-01-01
    • 2017-11-05
    • 2012-03-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多