mardi 31 mai 2011

Fonction d’exportation simple

Beaucoup d’applications proposent des fonctionnalités d’exportation de grilles de données vers des formats tels que CSV ou Excel. L’objectif de cet article est de proposer la fonction la plus simple et la plus générique possible pour répondre à cette problématique courante.

Ainsi, pour rendre la fonction la plus générique possible :

  • La réflexion est utilisée pour détecter dynamiquement les propriétés
  • Des paramètres optionnels (avec des valeurs par défaut) permettent d’utiliser la même fonction en faisant varier les détails d’implémentation
  • La fonction est une méthode d’extension sur IEnumerable<T>, ce qui la rend applicable dans quasiment tous les cas d’utilisation.

La fonction

Voici la fonction principale d’export :

public static class ExportationExtensions
{
public static string Export<TSource>(this IEnumerable<TSource> items,
bool includeTitleRow = true,
string header = "", string footer = "",
string rowPrefix = "", string rowSuffix = "",
string rowSeparator = "\r\n", string itemSeparator = ",",
Type ignoreFieldHavingAttributeType = null)
{
// serializable properties
var serializableProperties = typeof(TSource).GetProperties().AsEnumerable();
if (ignoreFieldHavingAttributeType != null)
serializableProperties = serializableProperties
.Where(pi => pi.GetCustomAttributes(false)
.All(o => o.GetType().FullName != ignoreFieldHavingAttributeType.FullName));

// result
return items.Aggregate(
string.Concat(header, serializableProperties
.Aggregate(rowPrefix,
(str, sp) => string.Concat(str, sp.Name, itemSeparator),
res => string.Concat(res, rowSuffix, rowSeparator))),
(row, item) => string.Concat(row, serializableProperties
.Aggregate(rowPrefix,
(str, sp) => string.Concat(str, sp.GetValue(item, null).ToString(), itemSeparator),
res => string.Concat(res, rowSuffix)), rowSeparator),
rows => string.Concat(rows, footer));
}
}



L’exportation produit par défaut des données au format CSV.


Utilisation


La fonction prévoit l’écriture éventuel de la ligne de titre, le préfixe et le suffixe de la chaine produite et de chaque ligne, ainsi que le séparateur entre chaque élément. En outre, afin de ne pas sélectionner certaines propriétés à exporter, la fonction élimine les propriétés sur lesquelles sont appliquées un attribut particulier (de la même manière que l’attribut XmlIgnore empêche la sérialisation XML du champ sur lequel l’attribut porte). Voici un exemple d’export d’une liste au format CSV puis sous forme de tableau HTML (ce qui permet l’ouverture directe par Excel par exemple) :

class Person
{
[DebuggerDisplay("")]
public string Nom { get; set; }
public string Prenom { get; set; }
public int Age { get; set; }
}

class Program
{
static void Main(string[] args)
{
List<Person> personnes = new List<Person>() {
new Person(){ Age = 55, Nom = "Gates", Prenom = "Bill"},
new Person(){ Age = 48, Nom = "Obama", Prenom = "Barack"},
new Person(){ Age = 26, Nom = "Damaj", Prenom = "Dany"}
};

Console.WriteLine(personnes.Export());

Console.WriteLine();

Console.WriteLine(personnes.Export(header: "<table>", footer: "</table>",
rowPrefix: "<tr><td>", rowSuffix: "</td></tr>",
itemSeparator: "</td><td>",
ignoreFieldHavingAttributeType: typeof(DebuggerDisplayAttribute)));

Console.ReadLine();
}
}



Champ d’application et perspectives


La fonction présentée ci-dessus a été écrite à l’occasion de la recherche d’une fonction d’Export CSV pour les tables Azure. Comme la plupart des fonctions très (trop?) génériques, ses performances ne sont pas excellentes en raison de l’utilisation de la réflexion et du type String en retour de la méthode (un fonctionnement par buffer serait préférable). D’autre part, le choix d’un séparateur entre éléments et d’un préfixe/suffixe pour chaque entité est celui qui semble être le plus logique, mais il ne convient sans doute pas à toutes les implémentations.

Aucun commentaire:

Enregistrer un commentaire