[C#] Colonnes dynamiques dans des requêtes SQL

J’ai récemment été confronté à un problème lors de l’exécution de requêtes SQL. Dans la partie Select de ces requêtes, nous avions des retours fixes (comme nous avons l’habitude) mais aussi des retours dynamiques (générés en fonction d’autres enregistrements de la base).

Exemple : Select Nom, Prenom, NomQuestion1,  NomQuestion2, … NomQuestionN

EntityFramework nous propose la méthode SqlQuery pour exécuter des requêtes Sql sur une base de données et nous retourner les valeurs. Cependant, la méthode nécessite de lui fournir une classe qui contient les mêmes propriétés (avec le même nom) que la requête exécutée sinon l’association de la valeur ne se fait pas. Et dans notre cas, il est difficile de fournir une classe ayant les propriétés nécessaires puisqu’elles sont dynamiques et peuvent avoir un nom dynamique …

Qui dit dynamique dit utilisation du type dynamic ! Je pensais avoir trouvé la solution en utilisant ce type que j’aurais ensuite traité pour le mettre dans ma propre classe de retour. Ensuite j’aurais mis en place une liste pour stocker les questions dynamiques. Mais SqlQuery ne prend pas en charge le type dynamic. J’ai donc trouvé une solution en utilisant un bon vieux ExecuteReader, de la Réflexion, et de la comparaison de nom.

La classe de base avec une liste de question :

public class PersonneQuestions
{
    public string Nom { get; set; }
    public string Prenom { get; set; }
    public List<Tuple<string,string>> Questions { get; set; }

    public PersonAndQuestions()
    {
        Questions = new List<Tuple<string, string>>();
    }
}

Et voici le code qui stocke les données retournées par la requête dans un objet du type PersonneQuestions :

using (var conn = db.Connection)
{
    // Ouverture de la connexion à la bdd
    conn.Open();

    // Execution de la requêtes
    var reader = cmd.ExecuteReader();

    // Exploitation des résultats tant qu'il y en a
    while (reader.Read())
    {
        var personneQuestions = new PersonneQuestions();

        // Pour toutes les valeurs retournés par la requête
        for (int i = 0; i < reader.FieldCount; i++)
        {
            // On récupère la valeur
            var columnName = reader.GetName(i);
            var columnValue = reader[i];

            // On recherche une propriété ayant le même nom dans notre classe (via reflexion)
            var prop = personneQuestions.GetType().GetProperty(columnName);

            if (prop != null)
            {
                // On définit cette valeur
                prop.SetValue(personneQuestions, Convert.ChangeType(columnValue, prop.PropertyType));
            }
            else
            {
                // Ici on entre dans le cas des champs dynamiques donc on fait un cas particulier (ici tous mes champs du Select se terminent par Question)
                if (columnName.EndsWith("Question"))
                {
                    personneQuestions.Questions.Add(new Tuple<string, string>(columnName, columnValue.ToString()));
                }
		else
		{
			Debug.WriteLine("On n'a pas trouvé de correspondance ni par reflexion ni dans le cas particulier");
		}
            }
        }

        peopleQuestions.Add(personneQuestions);
    }

    // Fermeture de la connexion à la bdd
    conn.Close();
}

Voila, si jamais vous rencontrez ce type de problématique voici une des solutions pour y répondre.

Bon code, et merci à Michel !

Photo de profil

Ces billets pourraient aussi vous intéresser

Vous nous direz ?!

Commentaires

comments powered by Disqus