Précédent   Forum du club des développeurs et IT Pro > Dotnet > Langages > C#
C# Forum d'entraide sur la programmation C#. Avant de poster -> FAQ C#, Articles C#, Sources C#
Partagez cette discussion sur d'autres réseaux sociaux : Viadeo Twitter Google Facebook Digg Delicious MySpace Yahoo
Réponse
 
Outils de la discussion
Publicité
'
Vieux 08/01/2013, 23h08   #1
dinguot
Membre habitué
 
Inscription : mars 2007
Messages : 114
Détails du profil
Informations forums :
Inscription : mars 2007
Messages : 114
Points : 101
Points : 101
Par défaut Ajouter des éléments à une liste par reflection

Bonjour,

j'essai d'initialiser une instance de classe à l'aide de la reflection.
Pour cela j'ai pris soin de nommer et typer les propriété de la classe du même noms et types que les colonnes de la table.

Cela fonctionne parfaitement pour les types simples. Par contre j'ai du mal à populer les listes.

voici mon code:
Code :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
 
    public class Calendar : Root
    {
        protected string _CalendarName;
 
        // Properties
        public string CalendarName { get { return _CalendarName; } }
        public List<string> SubCal { get; set; }
        public List<DateTime> Holidays { get; set; }
 
        // Constructor
        public Calendar(string name) 
        {
            Dictionary<string, Object> keys = new Dictionary<string, Object>();
            keys.Add("CalendarName", name);
            loadFromDB("subcalendar", keys);
            loadFromDB("calendarview", keys);
        }
    }
Code :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
 
    public class Root
    {
        private string _connectionString;
 
        public Root()
        {
            string bdd = ConfigurationManager.AppSettings["BDD"];
            _connectionString = ConfigurationManager.ConnectionStrings[bdd].ConnectionString;
        }
 
        private string buildClause(KeyValuePair<string, Object> kvp)
        {
            PropertyInfo prop = this.GetType().GetProperty(kvp.Key);
            string clause = "";
            string typeName = this.GetType().GetProperty(kvp.Key).PropertyType.Name;
 
            switch (typeName)
            {
                case "String":
                    clause = kvp.Key + " = '" + Convert.ToString(kvp.Value) + "'";
                    break;
                case "Boolean":
                    clause = kvp.Key + " = " + (Convert.ToBoolean(kvp.Value) ? "true" : "false");
                    break;
                case "DateTime":
                    clause = kvp.Key + " = '" + Convert.ToDateTime(kvp.Value).ToString("yyyy-MM-dd") + "'";
                    break;
                default:
                    throw new UnknownTypeException("Unknown type : " + typeName);
            }
            return clause;
        }
 
        private object getValue(MySqlDataReader reader, Type type, int col)
        {
            switch (type.Name)
            {
                case "String":
                    return reader.GetString(col);
                case "DateTime":
                    return reader.GetDateTime(col);
                case "Boolean":
                    return reader.GetBoolean(col);
                default:
                    throw new UnknownTypeException("Unknown type : " + type.Name);
            }
        }
 
        public void loadFromDB(string tableName, Dictionary<string, Object> keys)
        {
            // Build SQL request from parameters
            string sql = "select * from " + tableName;
            bool first = true;
 
            foreach (KeyValuePair<string, Object> kvp in keys)
            {
                sql += (first ? " where " : " and ") + buildClause(kvp);
                first = false;
            }
 
            // Execute sql request
            MySqlConnection myConnection = new MySqlConnection(_connectionString);
            MySqlCommand myCommand = myCommand = new MySqlCommand(sql, myConnection);
            myConnection.Open();
            MySqlDataReader reader = myCommand.ExecuteReader();
 
            // Parse result
            if (reader.HasRows)
            {
                // Here we know there is at least one row
                // First get the objects property
                PropertyInfo[] props = this.GetType().GetProperties();
                // The dictionnary will store the corresponding column number in the reader result
                Dictionary<PropertyInfo, int> columns = new Dictionary<PropertyInfo, int>();
 
                // Parse the properties to get the corresponding column number
                foreach (PropertyInfo prop in props)
                {
                    try
                    {
                        // Store the pair in the dictionnary
                        columns.Add(prop, reader.GetOrdinal(prop.Name));
                    }
                    catch (IndexOutOfRangeException e)
                    {
                        // current property does exist as column is this table
                        // ignore this error
                    }
                }
 
                // For each result row
                while (reader.Read())
                {
                    // Parse the properties to fetch them from reader data
                    foreach (KeyValuePair<PropertyInfo, int> column in columns)
                    {
                        // get the property type
                        Type type = column.Key.PropertyType;
                        // Check if it is a List
                        if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(List<>))
                        {
                            // Get the list item type
                            Type itemType = type.GetGenericArguments()[0];
                            // Get the list to add the value
                            var col = column.Key.GetValue(this, null) as IList;
                            if (col != null) // --> col is always null 
                                col.Add(getValue(reader, itemType, column.Value));
                            else
                                throw new InvalidOperationException("Not a list");
                        }
                        else
                        {
                            // If not just fetch the property or the corresponding field
                            if (column.Key.CanWrite)
                                column.Key.SetValue(this, getValue(reader, type, column.Value), null);
                            else
                                this.GetType().GetField("_" + column.Key.Name, BindingFlags.NonPublic | BindingFlags.Instance).SetValue(this, getValue(reader, type, column.Value));
                        }      
 
                    }
                }
            }
        }
    }
}
et enfin le contenu des tables pour un calendrier

subcalendar:
CalendarName SubCal
000 London
000 New-York

calendarview:
CalendarName Holidays
000 05/11/2013
000 04/07/2013

Malheureusement, dans le la classe Root, je n'arrive pas à récupérer l'instance de List pour y ajouter un élément. J'obtiens toujours un pointeur null.

une idée pour me dépanner ?
dinguot est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 09/01/2013, 16h24   #2
Babyneedle
Membre éclairé

 
Inscription : juin 2002
Messages : 283
Détails du profil
Informations personnelles :
Localisation : Canada

Informations forums :
Inscription : juin 2002
Messages : 283
Points : 363
Points : 363
Laisse-moi reformuler.

Tu veux prendre un résultat brut sous forme de Resultset d'ADO.Net et créer une instance d'une classe en faisant la correspondance entre les propriétés de cette classe et le nom des colonnes dans le dataset?
Babyneedle est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 09/01/2013, 16h37   #3
dinguot
Membre habitué
 
Inscription : mars 2007
Messages : 114
Détails du profil
Informations forums :
Inscription : mars 2007
Messages : 114
Points : 101
Points : 101
c'est tout à fait ça.

La requête renvoie plusieurs car on veut ajouter à la liste correspondante (SubCal ou Holidays) la valeur de la colonne du même nom
dinguot est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 09/01/2013, 16h57   #4
Babyneedle
Membre éclairé

 
Inscription : juin 2002
Messages : 283
Détails du profil
Informations personnelles :
Localisation : Canada

Informations forums :
Inscription : juin 2002
Messages : 283
Points : 363
Points : 363
Je n'ai pas de solution spécifique pour ce que tu veux faire, par contre voici une classe que j'ai faite pour adapter une instance de classe vers une nouvelle instance d'un type donné en faisant la correspondance entre les propriétés.

Code :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
 
public static class Adapter
    {
        /// <summary>
        /// Generic converter that will create an instance of type and fill properties that share same name (not case sensitive)
        /// </summary>
        /// <typeparam name="T">The type of instance wanted</typeparam>
        /// <param name="entity">The entity to be converted</param>
        /// <returns></returns>
        public static T ConvertTo<T>(this object entity)
        {
 
            // Create return instance
            var newEntity = (T)Activator.CreateInstance(typeof(T));
 
            if (entity != null)
            {
                // Loop through properties
                Parallel.ForEach(entity.GetType().GetProperties(),
                    property =>
                    {
                        // If accessible and has no parameters
                        if (property.CanRead && !(property.GetIndexParameters().Length > 0))
                        {
                            // Match properties by name
                            PropertyInfo other = typeof(T).GetProperty(property.Name);
                            if ((other != null) && (other.CanWrite))
                                other.SetValue(newEntity, property.GetValue(entity, null), null);
                        }
                    });
            }
 
            return newEntity;
        }
 
    }
Si tu peux parcourir en boucle ton dataset, il suffira de faire quelque chose comme ceci:

Code :
1
2
3
4
5
6
7
8
 
 
        public virtual IMyClass GetData()
        {
            var liste = new List<MaClasse>();
            Parallel.ForEach(DB.GetMyData(sql), result => { liste.Add(result.ConvertTo<MaClasse>()); } );
            return liste;
        }
Il suffirait de mofidier ce code pour travailler avec des noms de colonnes plutôt que des propriétés.

Sinon, tu peux aussi regarder le code source d'un ORM open source tel que celui-ci.

http://code.google.com/p/servicestack/source/checkout
Babyneedle est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 09/01/2013, 17h25   #5
dinguot
Membre habitué
 
Inscription : mars 2007
Messages : 114
Détails du profil
Informations forums :
Inscription : mars 2007
Messages : 114
Points : 101
Points : 101
Merci pour le tuyau.

Je vais aussi investiguer du côté de LINQ to SQL pour essayer de régler mon problème sans code ou presque
dinguot est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 10/01/2013, 21h31   #6
dinguot
Membre habitué
 
Inscription : mars 2007
Messages : 114
Détails du profil
Informations forums :
Inscription : mars 2007
Messages : 114
Points : 101
Points : 101
bon j'suis trop bête.

mon code fonctionne parfaitement à condition d'allouer les listes dans le constructeur de Calendar ...
dinguot est déconnecté   Envoyer un message privé Réponse avec citation 00
Réponse Cette discussion est résolue.
Outils de la discussion

Navigation rapide


Fuseau horaire GMT +2. Il est actuellement 20h20.


 
 
 
 
Partenaires

Hébergement Web