Création de fichiers Zip avec .NET 4.5

Le framework .NET offre beaucoup de fonctionnalités aux développeurs afin d’éviter à avoir à écrire et réécrire certaines fonctionnalités avec le temps. Un domaine qui a toujours manqué à .NET à travers le temps est au niveau de la compression de fichiers.

Sans avoir recours à une librairie externe, il y a les deux choix qui s’offraient au développeur :

Le tout est bien et assez fonctionnel. Dans les deux cas, il est possible de prendre un fichier en mémoire et de le compresser pour ainsi économiser en espace lors du transport.

J’ai toujours trouvé l’opération assez fastidieuse et très répétitive. Sommairement, compresser un fichier équivalait à :


namespace Compression
{
public class Compression
{
public static void Compress(FileInfo fi)
{
using (FileStream inFile = fi.OpenRead())
{
if ((File.GetAttributes(fi.FullName) & FileAttributes.Hidden) != FileAttributes.Hidden & fi.Extension != ".gz")
{
using (FileStream outFile = File.Create(fi.FullName + ".gz"))
{
using (GZipStream Compress = new GZipStream(outFile, CompressionMode.Compress))
{
inFile.CopyTo(Compress);
}
}
}
}
}
}
}

view raw

gistfile1.cs

hosted with ❤ by GitHub

Prenez note que j’ai laissé la mention du Namespace pour avoir encore plus une plus belle pyramide de code!

Le vrai problème est que l’algorithme Deflate est utilisé pour les fichiers Zip mais la classe DeflateStream ne fournit pas les entêtes de fichiers nécessaires pour la création de fichiers Zip. Cela n’est pas très pratique.

L’alternative à tout ça aura été la librairie DotNetZip. Cette librairie a été  le choix de-facto afin de compresser des fichiers dans le format Zip. Le résultat est à la hauteur sans artifices et à la hauteur des attentes  :


using (ZipFile zip = new ZipFile())
{
zip.AddFile("c:\\images\\personal\\7440-N49th.png", "images");
zip.AddFile("c:\\Reports\\2008-Regional-Sales-Report.pdf", "files");
zip.AddFile("ReadMe.txt");
zip.Save("MyZipFile.zip");
}

view raw

gistfile1.txt

hosted with ❤ by GitHub

La nouveauté

Avec .NET 4.5, cette époque est révolue. La gestion de fichier archives .NET est maintenant intégrée directement dans le Framework sous le namespace System.IO.Compression.

Au total, il y a quatre classes vous permettant de faire la gestion de vos archives. À chacune son rôle bien spécifique :

  • ZipFile
    • Offre les méthodes nécessaires pour la création, extraction et ouverture des fichiers Zip.
  • ZipFileExtensions
    • Ajoute certaines extensions aux fonctionnalités pour la gestion des archives compressées. Par exemple. il est possible de compresser un répertoire entier ou même d’ajouter des fichiers à une archive Zip existante.
  • ZipArchive
    • Représente une archive Zip. Le conteneur des fichiers compressés.
  • ZipArchiveEntry
    • Représente un fichier à l’intérieur d’une archive Zip.

Exemples

La manipulation de l’API est très simple. Il faut reconnaître que l’équipe du développement de .NET a fait du beau travail avec la gestion des archives Zip.

Notez que, dans tous les cas, il est important d’ajouter les références à System.IO.Compression ainsi que System.IO.Compression.FileSystem pour que le tout puisse compiler.

Création


using (ZipArchive archive = ZipFile.Open(@"c:\frenchcoding\start.zip", ZipArchiveMode.Create))
{
archive.CreateEntryFromFile(@"c:\frenchcoding\test.txt", "test_compress.txt");
archive.CreateEntryFromFile(@"c:\frenchcoding\test2.txt", "test_compress2.txt");
archive.CreateEntryFromFile(@"c:\frenchcoding\test3.txt", "test_compress3.txt");
}

view raw

gistfile1.cs

hosted with ❤ by GitHub

Extraction


using (ZipArchive archive = ZipFile.Open(@"c:\frenchcoding\start.zip", ZipArchiveMode.Read))
{
archive.ExtractToDirectory(@"c:\frenchcoding\extract\");
}

view raw

gistfile1.cs

hosted with ❤ by GitHub

Suppression d’éléments


using (ZipArchive archive = ZipFile.Open(@"c:\frenchcoding\start.zip", ZipArchiveMode.Update))
{
archive
.GetEntry("test_compress2.txt")
.Delete();
archive
.GetEntry("test_compress3.txt")
.Delete();
}

view raw

gistfile1.cs

hosted with ❤ by GitHub

Expérimentation avec System.Tuple – Du code sans DTO conventionnels?

Par définition, un DTO (Data Transfer Object) permet l’échange de données à travers différentes couche logicielles d’une application. À titre d’exemple, un DTO permet de représenter par une structure de données l’information qui va provenir de votre base de données. L’utilisation est au coeur même du principe qui permet d’écrire du code réutilisable et facilement maintenable.

Tuple, c’est quoi ça?

Un Tuple ou même n-uplet en français est une structure de données permettant de regrouper des types hétérogènes ensemble. Pour ceux plus familier avec l’aspect mathématique de la chose, un Tuple est le concept de Couple étendu à N types.

Avec .NET, il y a huit variations de la classe Tuple. Chacune des variations a son nom qui la distingue :

  • Tuple<T1> : Singleton
  • Tuple<T1, T2> : Couple
  • Tuple<T1, T2, T3> : Triplet
  • Tuple<T1, T2, T3, T4> : Quadruplet
  • Tuple<T1, T2, T3, T4, T5> : Quintuplet
  • Tuple<T1, T2, T3, T4, T5, T6> : Sextuplet
  • Tuple<T1, T2, T3, T4, T5, T6, T7> : Heptuplet
  • Tuple<T1, T2, T3, T4, T5, T6, T7, T8> : Octuplet
Il est possible de créer un Tuplet considéré comme infini ayant la signature Tuple<T1, T2,T3, T4, T5, T6, T7, TRest>. Il est considéré infini car le dernier type TRest peut être utilisé pour imbriquer d’autres Tuplets. Il n’y a pas de limite théorique au nombre de Tuplets qui peuvent être imbriqués.
Un Tuple ayant la signature Tuple<string, string, string, int, Image> peut se créer de la façon suivante, par exemple :


var tuple = Tuple.Create("Pascal", "Paradis", "pascal.paradis@gmail.com", 26, Image.FromFile("pparadis_avatar.png"));

view raw

gistfile1.cs

hosted with ❤ by GitHub

Le concept proposé

Mon but initial lorsque j’ai lu à propos de Tuple serait de savoir s’il serait réellement pratique de l’utiliser avec ExpandoObject pour transporter un certain nombre de données.

Avec la venue de la classe ExpandoObject dans .NET4, il est possible d’ajouter dynamiquement des propriétés à un objet. L’idée m’est venue de tenter de combiner, le temps d’un essai, de combiner ExpandoObject et Tuple ensemble.

Le concept que j’ai développé est disponible pour consultation sur GitHub. Il y a deux méthodes qui sont à regarder : TupleExemple.(Tuple<string, string, string, int, Image> user) et UserRepository.GetRegisteredUserList(). De plus, l’appel à ces deux méthodes est fait dans le constructeur de TupleExemple.

Le constat

Initialement, lorsque j’ai pondu ce code, j’ai été sur l’impression d’avoir trouvé une nouvelle façon d’écrire du code. Mon impression était, qu’avec un nombre limité de propriétés à transporter, un Tuple serait un excellent moyen de transport. Le seul facteur qui me permettait d’affirmer cela est que, dans ce cas-ci, je n’aurais pas à eu besoin de recourir à une classe servant de DTO.

Après relecture de ParseUserInformations, je crois qu’il faut utiliser beaucoup de jugement lorsqu’il est temps d’utiliser un Tuple pour exposer une structure de données qui sera au cœur de votre application. Sur 50 lignes de code, il est facile de se souvenir qu’Item3 est relié au courriel. Toutefois, qu’en sera-t-il lorsque vous tenterez de le faire la même chose avec 10 000 lignes de code?

À mon avis, un scénario idéal pour utiliser Tuple, serait dans le cadre d’opérations très ciblés ayant un contexte précis. Ce que j’ai souvent en tête, c’est une classe en charge de l’importation d’une liste d’utilisateurs dans votre base de données.

Le but serait d’avoir, au sein de la même classe ou méthode, tout ce qu’il faut pour récupérer la source de données originale. Soit la représenter avec un Tuple, faire les transformations nécessaires et l’importer dans votre base de données.

Un hommage à String.IsNullOrEmpty()

Il faut du recul pour se donner de la perspective sur le chemin qui a été parcouru dans les dernières années. En tant que développeur, il est force d’admettre que les choses évoluent dramatiquement rapidement.

Du côté de .NET, vous vous souvenez de l’époque de .NET 1.1? Comparé à ce qui est possible de faire aujourd’hui, il est à se demander comment il était même possible d’arriver à développer avec ce Framework là!

De mémoire, vous vous souvenez ce qui vous a marqué le plus avec la version 2.0 de .NET? Est-ce que ce serait l’un des points suivants ?

Providers

  • Membership
  • Role
  • Sitemap
  • Profile
  • Session
  • Web events
  • Web Parts

ASP.NET Code behind

/app_code ?

Generics?

ASP.NET Thèmes et skins ou même les Master Pages ?

Classes partielles

Itérateurs?

Non!

Pour moi, l’amélioration la plus marquante a été String.IsNullOrEmpty() aura été celle que j’utilise le plus dans tout le Framework. Elle fait, dans une très grande majorité de cas, partie de la première validation sur l’état d’une string lorsqu’on la reçoit en paramètre.

Son implémentation est très simple. Par son nom, il est très facile de déterminer son but : Est-ce que cette chaîne de caractères est de type null ou vide?

Sommairement, cette fonction permet de convertir le code suivant :

var isValid = (maString== null || maString.Length == 0);

de cette façon :

var isValid =  (string.IsNullOrEmpty(maString));

Un hommage

À la blague, j’ai même démarré avec la collaboration d’un ami un micro-blogue en hommage à String.IsNullOrEmpty(). Un billet que j’aime particulièrement est l‘internationalisation de String.IsNullOrEmpty(). L’article met en lumière qu’il n’est quand même pas si simple d’écrire cette même validation en Javascript.


String.IsNullOrEmpty = function(value) {
var isNullOrEmpty = true;
if (value) {
if (typeof (value) == 'string') {
if (value.length > 0)
isNullOrEmpty = false;
}
}
return isNullOrEmpty;
}

view raw

gistfile1.js

hosted with ❤ by GitHub

La validation poussée à son maximum

Lorsque j’ai eu à chercher des idées pour ce billet, j’ai trouvé le gist suivant sur Github (note : ne pas faire lire aux enfants) :

https://gist.github.com/1583796

Élégant, clair, très expressif et surtout vulgaire. Adorable! Pourquoi faire simple lorsqu’on peut littéralement inclure la totalité des validations de String dans la même fonction?

Conclusion

Je vais l’avouer, mon affection envers String.IsNullOrEmpty est légèrement démesurée.

Toutefois, à quand revient le dernier moment où vous avez écrit une validation pour déterminer si votre String était null ou vide à l’ancienne? Probablement que ça date de l’instant où vous avez découvert que la fonction existait.

Si vous êtes arrivés sur le billet et que vous venez d’apprendre l’existence même de cette fonction, je crois alors que ma mission de prêcher la bonne nouvelle aura été accomplie!

Alternatives pour faire du SQL avec .NET et C#

Lorsqu’il est venu le temps du choix de choisir le moyen de stocker les données de son application, le temps est à la mode de délaisser les bases de données relationnelles pour leur équivalent NoSQL.

Dans le monde .NET, il est possible d’utiliser des technologies comme MongoDB ou même RavenDB qui est de plus en plus populaire chez les programmeurs.

Une base données NoSQL comme RavenDB ne convient pas nécessairement à tous les scénarios de développement. Qu’en est-il de ceux qui désirent utiliser une base de données SQL? Est-ce qu’il y a des alternatives à l’utilisation de la classe SqlCommand?

Pour ces scénarios, j’ai arrêté mon choix à deux candidats qui font leur marque en utilisant deux approches différentes. Tout en ayant comme but de faciliter l’accès à vos bases de données.

Massive

Massive est une librairie développée par le très populaire Rob Conery. Cette librairie se vend comme l’outil vous permettant de résoudre la majorité de vos besoins d’accès de données avec un seul fichier de 673 lignes.

La simplicité de conception de Massive.cs est son principal atout. Il suffit d’intégrer le fichier source à votre application et vous serez en affaires. Notez qu’il vous faut une Connection string de proprement configurée à votre application.

Tel que décrit par son créateur, il faut concevoir Massive comme un Wrapper vous permettant d’accéder à vos données SQL. Son design vise une approche rapide sans dépendances pour accéder à vos donnés

Son utilisation est très simple et par les exemples disponibles sur la page d’accueil du projet GitHub permettent d’emblée de constater que Massive est conçu autour des dynamics ajoutés dans .NET 4.0.

Par exemple, il est possible de faire des requêtes à votre base de données en demandant à Massive de deviner vos intentions par convention.


//tiré de https://github.com/robconery/massive#code-please
dynamic table = new Products(); //"dynamic" is important here – don't use "var"!
var productsFour = table.Find(CategoryID:4,columns:"ProductName");

view raw

gistfile1.cs

hosted with ❤ by GitHub

Cette requête va retourner, sous une liste d’éléments, le résultat de la colonne ProductName de la table Products appartenant au CategoryID 4.

Selon la documentation, en plus de pouvoir faire des requêtes directes à vos données, vous pouvez arriver à :

Il est possible d’utiliser Massive avec les bases de données suivantes :

Simple.Data

Simple.Data se présente comme outil d’accès aux données dynamique ayant des fonctionnalités retrouvées dans un ORM plus classique.

Tout comme Massive, la composante dynamic de .NET 4.0 est au cœur de l’architecture de Simple.Data. Les requêtes que le développeur va construire seront décodées par convention afin d’y extraire les actions ainsi que les informations qui y sont désirées.

Simple.Data se veut comme un langage dédié (Un DSL pour ceux qui sont plus familiers avec l’anglais). Les requêtes sont, d’une certaines façon, paraphrasées en mots et interprétées par la librairie au moment de l’exécution.  À titre d’exemple, une requête permettant de retrouver un utilisateur par son identifiant courriel serait la suivante : Database.Open().Users.FindByEmail(email);.

Simple.Data support une un vaste éventail de plateformes. Autant des bases de données relationnelles traditionnelles que des bases de données NoSQL. Voici la liste :

Du point de vue des fonctionnalités, Simple.Data est assez complet et permet d’effectuer une gamme assez vaste de commandes et d’opérateurs vous permettant d’accéder à vos données. Bien entendu, l’insertion, la mise à jour ainsi que la suppression de données est permise.

D’ailleurs, sa nature dynamique permet à Simple.Data d’être un candidat de choix pour faire des tests unitaires. Un adaptateur de données uniquement en mémoire permettant de faire des requêtes sans avoir à accéder à une base de données réelle.

Conclusion

Les deux librairies ne sont pas réellement en compétition l’une envers l’autre. À mon avis, elles répondent à un besoin bien spécifique selon le genre d’opérations que vous désirez compléter.

Massive me semble pratique lorsqu’il est temps d’accéder à rapidement à des données sans trop se soucier de la réutilisation ou de l’architecture de votre couche d’accès aux données. On voit souvent cette application dans un contexte de preuve de concept.

Simple.Data se veut comme une alternative à un ORM que vous pouvez utiliser à toutes les sauces dans votre système lorsqu’il est temps de gérer l’accès à vos données. On remarque cette tendance par le soucis de testabilité de Simple.Data.

C# Dynamics

La dernière mise à jour majeure du Framework .NET a ajouté un concept ajoutant une couche permettant à C# d’agir comme un langage dynamique. Un langage dynamique permet d’être interprété qu’au moment de son exécution.

Dans les langages dits dynamiques, on retrouve des noms connus comme :

Un language dynamique fait contraste avec un language fortement typé où le code est exécuté au moment de la compilation de celui-ci.

Introduction du type dynamic dans .NET 4.0

Le type dynamic a été introduit à 4.0 afin de court circuiter  la validation statique des types. Cet ajout permet de d’écrire du code qui sera résolu une fois seulement qu’il sera exécuté.

Ce nouveau mot clé peut être utilisé à plusieurs sauces. Dans certains cas, son utilisation va sembler assez transparente. Par exemple, les types dynamics sont utilisés assez régulièrement avec ASP.NET MVC avec la mécanique du ViewBag.

Un bel exemple de la différence entre les deux concepts est le code suivant :


var x = 10;
var y = "2";
var result = x * y;

view raw

gistfile1.js

hosted with ❤ by GitHub

Le code exécuté en javascript va donner le résultat « 20 » car il s’agit de la multiplication de 10 et de 2. Le même code en C# ne passera pas l’étape de la compilation car il n’est pas possible de multiplier un nombre à une chaîne.

ExpandoObject

Une instance de la classe ExpandoObject vous permet d’avoir accès à un objet totalement dynamique dans lequel vous pouvez y enregistrer l’information que vous y désirez dans des propriétés. Ces propriétés peuvent être aussi être parcourues à l’aide d’une requête.

L’ExpandoObject expose un dictionnaire ayant la signature IDictionary<String, Object>) afin que vous puissiez y retrouver vos propriétés dans une boucle par exemple. Vous pouvez le faire de cette façon :


dynamic expando = new ExpandoObject();
expando.Title = "Ceci est un titre";
expando.Nom = "Paradis";
expando.Prenom = "Pascal";
foreach (var e in (IDictionary<String, Object>) expando)
{
Console.WriteLine(e.Value);
}

view raw

gistfile1.cs

hosted with ❤ by GitHub

Ce code va écrire à la console le texte suivant :

Ceci est un titre
Paradis
Pascal

DynamicObject

Considérant l’implémentation vraiment naive d’un objet servant de dictionnaire d’URL :


dynamic urlDictionnary = new UrlDictionnary();
urlDictionnary.HomePage = "http://www.parad.is&quot;;
urlDictionnary.About = "https://frenchcoding.wordpress.com/about/&quot;;
urlDictionnary.Blog = "https://frenchcoding.wordpress.com&quot;;

view raw

gistfile1.cs

hosted with ❤ by GitHub

Ce qui est important de savoir avec ce dictionnaire c’est qu’il hérite de la classe DynamicObject. DynamicObject permet à  une classe exposant des propriétés ou des fonctionnalités comme un dictionnaire ou une liste d’exposer ses propriétés de façon dynamique.

Dans le cas de cet exemple, il n’est pas question sa savoir quel algorithme de tri dictionnaire d’URL utilise pour trier ses élément mais plus de savoir qu’il est possible d’accéder dynamiquement à ses propriétés.

Pour avoir un minimum de fonctionnalités avec votre classe héritant de DynamicObject, il vous faut réimplémenter les fonctions TryGetMember et TrySetMember. Ces deux réimplémentations vont vous permettre d’avoir les fonctionnalités  vous permettant d’accéder et de paramétrer des propriétés dynamiques.

Voici un exemple excessivement simpliste vaguement tiré de MSDN


public class UrlDictionnary : DynamicObject
{
Dictionary<string, string> dictionary = new Dictionary<string, string>();
public int Count
{
get
{
return dictionary.Count;
}
}
public override bool TryGetMember(GetMemberBinder binder, out object result)
{
var keyName = binder.Name.ToLower();
if(dictionary.ContainsKey(keyName))
{
result = dictionary[keyName];
return true;
}
result = null;
return false;
}
public override bool TrySetMember(SetMemberBinder binder, object value)
{
dictionary[binder.Name.ToLower()] = (string) value;
return true;
}
}

view raw

gistfile1.cs

hosted with ❤ by GitHub