Du bon usage des méthodes d’extension en C# (partie 2)

Nicholas Suter Nicholas Suter
décembre 13, 2012
Pas de commentaire
C#

Nous avons vu dans la première partie un contre-exemple de l’usage des méthodes d’extension de C#. Mais l’idée n’est évidemment pas de diaboliser leur utilisation. Au contraire, si les méthodes d’extension sont puissantes, elles se révèlent parfois extrêmement utile.

En l’occurrence, il y a un usage que j’aime beaucoup, et qui consiste à coder en quelques lignes un embryon d’ORM. Partons du prérequis ayant mené au développement du code que nous verrons un peu plus bas : PAS D’ORM au sens strict du terme. C’est dans ce cas de figure une règle d’architecture. Donc nous devons coder à la main les appels à la base via ADO.NET.

Nous devons donc coder nous-mêmes nos procédures stockées et alimenter des classes POCO. Et faire le mapping à la main se révèle rapidement fastidieux. C’est ici qu’apparait le chaînon manquant.

Nous allons donc étendre l’interface IDataReader avec une méthode ConvertToPoco qui convertira notre DataReader en un IEnumerable<T> où T sera notre classe POCO. Pour faire simple, nous allons adopter une approche de codage par convention. Le nom des colonnes retournées par notre requête SQL devra être identique au nom des propriétés publiques de notre POCO.

Code highlighting produced by Actipro CodeHighlighter (freeware)
http://www.CodeHighlighter.com/

–> 1 using System; 2 using System.Collections.Generic; 3 using System.Data; 4 5 namespace ExtensionMethods 6 { 7 8 publicclass SqlDataReaderMapping 9 { 10 ///<summary>11 /// Converts an existing IDataReader to objects. 12 ///</summary>13 ///<typeparam name= »T »></typeparam>14 ///<param name= »reader »>The IDataReader to be converted</param>15 ///<returns></returns>16 public IEnumerable<T> ConvertToPoco<T>(IDataReader reader) where T : class, new() 17 { 18 var list =new List<T>(); 19 T t; 20 21 while ((t = UnitConvertToPoco<T>(reader)) !=default(T)) 22 { 23 list.Add(t); 24 } 25 26 return list; 27 } 28 29 30 public T UnitConvertToPoco<T>(IDataReader reader) where T : class, new() 31 { 32 if (reader.Read() ==false) 33 returndefault(T); 34 35 Type prototype =typeof(T); 36 37 if (prototype.IsAbstract) 38 { 39 thrownew Exception(« abstract class can’t be instantiated and mapped« ); 40 } 41 42 // use reflection to instantiate a new object43 var poco = Activator.CreateInstance<T>(); 44 45 46 foreach (var propertyInfo in prototype.GetProperties()) 47 { 48 var column = propertyInfo.Name; 49 50 // check if data reader has this column51 if (reader.HasColumn(column)) 52 { 53 // the datareader contains a column with the same name as the current property54 var value = reader[column]; 55 propertyInfo.SetValue(poco, (value == DBNull.Value) ?null : Convert.ChangeType(value, propertyInfo.PropertyType), null); 56 } 57 } 58 59 return poco; 60 } 61 } 62 }

Le code est donc lui-même relativement simple. Nous utilisons ici des notions basiques de réflexion afin de générer un objet de type générique. La méthode ConvertToPoco itère à travers les lignes de notre DataReader. Pour chacune des lignes du DataReader, nous appelons la méthode UnitConvertToPoco, et nous alimentons une List<T>.

La méthode UnitConvertToPoco est également d’une simplicité assez redoutable. Nous instancions tout d’abord un nouvel objet de type T. Ensuite, nous itérons à travers les différentes propriétés publiques de la classe T. Si le DataReader possède une colonne de même nom que la propriété, nous alimentons cette propriété avec la valeur de la colonne. Et puis c’est tout.

Ce n’est pas l’objet de cet article, mais il est possible d’enrichir ces méthodes de façon à savoir gérer des champs en plus des propriétés, ou encore de décorer nos DTO d’attributs de façon à pouvoir affiner le peuplement (pour ignorer une propriété, par exemple).

Imaginons d’une côté une classe :

1 using System; 2 3 namespace ExtensionMethods.Tests 4 { 5 publicclass MaClassePoco 6 { 7 publicint Id { get; set; } 8 publicstring Name { get; set; } 9 public DateTime SomeDate { get; set; } 10 } 11 }

Et pour terminer, voici comment s’utilise la méthode :

1 using System.Collections.Generic; 2 using System.Data; 3 using System.Data.SqlClient; 4 using Microsoft.VisualStudio.TestTools.UnitTesting; 5 6 namespace ExtensionMethods.Tests 7 { 8 [TestClass] 9 publicclass UnitTest1 10 { 11 [TestMethod] 12 publicvoid TestMethod1() 13 { 14 conststring connectionString =« MyConnectionString« ; 15 IEnumerable<MaClassePoco> listPocos; 16 17 using (var c =new SqlConnection(connectionString)) 18 { 19 c.Open(); 20 21 using (var cmd =new SqlCommand(@ »PS_GET_POCOS« , c)) 22 { 23 cmd.CommandType = CommandType.StoredProcedure; 24 listPocos = cmd.ExecuteReader().ConvertToPoco<MaClassePoco>(); 25 } 26 } 27 28 foreach (MaClassePoco poco in listPocos) 29 { 30 Assert.IsTrue(poco.Id !=0); 31 } 32 } 33 } 34 } 35

A l’aide d’une méthode d’extension de l’interface IDataReader, sur laquelle nous n’avons évidemment pas la main, nous avons implémenté une nouvelle fonctionnalité à la syntaxe élégante.

Nous avons donc un exemple et un contre-exemple de l’utilisation des méthodes d’extension. L’équilibre cosmique est préservé, soyons en paix avec nos chakras.

Tags: C#,

Pas de commentaire

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *