winforms C # .NET - Comment améliorer les performances de mon code tout en utilisant des boucles (for, foreach, etc…) [fermé]

Jan 29 2021

Je développe une application pour les collèges, une partie de l'application consiste à définir le calendrier hebdomadaire des cours.

Mes paramètres sont le numéro de classe, le jour de la semaine et le créneau horaire.

J'ai donc une gamme de salles de classe, chaque classe a un numéro.

dans l'exécution, l'application génère des boutons en fonction du nombre de salles de classe que j'ai sur ma base de données et définit les numéros de classe sur chaque bouton.

Ce que je veux faire, c'est étiqueter chaque bouton de la classe avec BackColor «rouge» si cette classe est pleine le jour de la semaine et le créneau horaire donnés.

J'ai accompli ce que je voulais faire et mon code fonctionne sans erreur, mais mon seul problème est maintenant la performance.

Voici mon code:

private OleDbConnection Connection = new OleDbConnection();


private void SomeMethod(string Day, string Time)
    {
        int MaxIndex = 0;
        string str1 = "select Max([Row Index]) from Table";       
      OleDbCommand Command1 = new OleDbCommand(str1, Connection);
        Connection.Open();
        if (Command1.ExecuteScalar() == DBNull.Value)
            MaxIndex = 1;

        else
            MaxIndex = Convert.ToInt32(Command1.ExecuteScalar());
        Connection.Close();

        for (int i = 0; i < MaxIndex; i++)
        {
            string str = "select [classroom Number] from Table where [Day] = @ParamDay and [Time] = @ParamTime and [Row Index] = @ParamIndex";

            OleDbCommand Command = new OleDbCommand(str, Connection);
            Command.Parameters.Add("@ParamDay", Day);
            Command.Parameters.Add("@ParamTime", Time);
            Command.Parameters.Add("@ParamIndex", i + 1);

            Connection.Open();
            OleDbDataReader reader = Command.ExecuteReader();

            if (reader.Read())
            {
                foreach (Button btn in ButtonsPanel.Controls)
                {
                    if (btn.Text == reader["classroom Number"].ToString())
                    {
                        btn.BackColor = Color.Red;
                    }

                }
                Connection.Close();
            }
        }


    }
    

donc ce code prend environ 13 secondes si j'ai 200 lignes que je m'attends à avoir.

La question est ... Est-ce que je peux faire quelque chose à mon code pour que ces 13 secondes se réduisent à au moins 2-4 secondes?

Pour information: j'ai beaucoup cherché sur Internet, mais je n'ai pas trouvé la solution à mon problème ici.

Réponses

AntonínLejsek Jan 29 2021 at 10:22

Vous ne semblez pas du tout avoir besoin du cycle for. Et le MaxIndexnon plus. Il suffit de télécharger les enregistrements pour l'heure et de marquer les boutons.

private void SomeMethod(string Day, string Time)
{
    HashSet<string> classNumbers = new HashSet<string>();

    string str = "select [classroom Number] from Table where [Day] = @ParamDay and [Time] = @ParamTime";
    using (OleDbCommand Command = new OleDbCommand(str, Connection))
    {
        Command.Parameters.Add("@ParamDay", Day);
        Command.Parameters.Add("@ParamTime", Time);                
        Connection.Open();
        using (OleDbDataReader reader = Command.ExecuteReader(CommandBehavior.CloseConnection))
        {
            while (reader.Read())
            {
                classNumbers.Add(reader["classroom Number"].ToString());
            }
        }
    }

    foreach (Button btn in ButtonsPanel.Controls)
    {
        if (classNumbers.Contains(btn.Text))
        {
            btn.BackColor = Color.Red;
        }
    }
}
GellioGao Jan 29 2021 at 09:37

Il y a deux choses que vous pouvez changer pour vous améliorer:

  1. Ouvrez et fermez la connexion une seule fois, ce qui peut réduire le temps d'exécution du code.
  2. Récupérez toutes les données que vous souhaitez traiter en une seule requête.

Consultez le code ci-dessous:

    private OleDbConnection Connection = new OleDbConnection();

    private void SomeMethod(string Day, string Time)
    {
        int MaxIndex = 0;
        string str1 = "select Max([Row Index]) from Table";       
        OleDbCommand Command1 = new OleDbCommand(str1, Connection);
        Connection.Open();
        if (Command1.ExecuteScalar() == DBNull.Value)
            MaxIndex = 1;
        else
            MaxIndex = Convert.ToInt32(Command1.ExecuteScalar());

        string str = "select [classroom Number] from Table where [Day] = @ParamDay and [Time] = @ParamTime and [Row Index] between 1 and @ParamIndex";

        OleDbCommand Command = new OleDbCommand(str, Connection);
        Command.Parameters.Add("@ParamDay", Day);
        Command.Parameters.Add("@ParamTime", Time);
        Command.Parameters.Add("@ParamIndex", MaxIndex);

        OleDbDataReader reader = Command.ExecuteReader();
        while (reader.Read())
        {
            foreach (Button btn in ButtonsPanel.Controls)
            {
                if (btn.Text == reader["classroom Number"].ToString())
                {
                    btn.BackColor = Color.Red;
                }
            }
        }

        Connection.Close();
    }
Hayden Jan 29 2021 at 10:28

Selon mes commentaires, vous n'avez besoin d'exécuter la requête qu'une seule fois et de parcourir l'ensemble de résultats. Cela aura des gains de performances puisque vous n'accédez maintenant qu'une seule fois à l'IO, car l'IO est généralement très lent.

Voici un exemple (non testé):

private void SomeMethod(string day, string time)
{
    // Using statement helps to dispose any resources once done with the connection
    // connectionString can be any string that opens your database
    using (OleDbConnection connection = new OleDbConnection(connectionString))
    {
        // The query has removed the notion of index, it will just get all the data for that day and time
        string query = "SELECT [classroom Number] FROM Table WHERE [Day] = @ParamDay AND [Time] = @ParamTime";

        // Since OleDbCommand inherits from IDisposable, use a using statement
        using (OleDbCommand command = new OleDbCommand(query, connection))
        {
            // Notice how we don't use index anymore
            command.Parameters.Add("@ParamDay", day);
            command.Parameters.Add("@ParamTime", time);

            // Open connection here, don't need to close connection
            connection.Open();

            // Since OleDbDataReader inherits from IDisposable, use a using statement
            using (OleDbDataReader reader = command.ExecuteReader())
            {
                // We're now looping through all the rows in the result set
                while (reader.Read())
                {
                    UpdateButtonColor(reader["classroom Number"].ToString());
                }
            }
        }
    }
}

private void UpdateButtonColor(string classroomNumber)
{
    foreach (Button btn in ButtonsPanel.Controls)
    {
        if (btn.Text == classroomNumber)
        {
            btn.BackColor = Color.Red;
        }
    }
}

Les nouvelles versions de C # permettent à l' usinginstruction de ne pas nécessiter d'accolades (ce qui réduit l'imbrication), ce qui ressemblerait à ceci:

private void SomeMethod(string day, string time)
{
    string query = "SELECT [classroom Number] FROM Table WHERE [Day] = @ParamDay AND [Time] = @ParamTime";
    using OleDbConnection connection = new OleDbConnection(connectionString);
    using OleDbCommand command = new OleDbCommand(query, connection);

    command.Parameters.Add("@ParamDay", day);
    command.Parameters.Add("@ParamTime", time);

    connection.Open();

    using OleDbDataReader reader = command.ExecuteReader();
    
    while (reader.Read())
    {
        UpdateButtonColor(reader["classroom Number"].ToString());
    }
}

La documentation à ce sujet peut être trouvée ici