Pourquoi TRON s’appelle TRON ?

Récemment Walt Disney nous a gratifié d’une de ses productions : Tron l’héritage
Il se trouve que je suis assez ancien pour avoir vu au cinéma Tron en 1982 et tous ceux qui sont assez anciens pour avoir vu le film original de 1982 savent ce que veut dire TRON.
Du moins ceux qui faisaient déjà de l’informatique…

A l’époque l’informatique individuelle ne ressemblait à rien à ce que l’on voit aujourd’hui :

•    pas d’écrans plats,
•    pas de souris,
•    pas d’écrans graphiques ou couleurs,
•    pas de disque dur, disquettes ou clef Usb (on enregistrait sur des K7, oui vous avez bien lu)
•    Pas de système d’exploitation
•    Certaines machines étaient livrées en pièce détachées à souder soi-même (qui se souvient du Kim ou du ZX 81 ???)
•    Pas d’Internet (non je n’ai pas fumé)
•    Pas de modem (je n’ai pas non plus bu)
•    Des machines à ultra haute teneur en incompatibilité (laissez tomber, je sais bien que vous ne comprenez même pas le mot)

Et puis surtout pas de langage de programmation à part l’assembleur !!!!!

Bon j’exagère un peu, en fait à cette époque un certains Bill Gates et son compère Paul Allen avaient une spécialité : dès qu’un microprocesseur sortait quelque part, Allen écrivait un émulateur sur le PDP 11 de la fac et Bill s’en servait pour écrire un compilateur BASIC adapté qu’ils s’empressaient de revendre aux fabricants utilisant ce microprocesseur. Parfois même dans l’avion qui les amenaient de leur Californie natale
vers l’usine de leur futur client !
Et c’était d’autant plus payant que leur implémentation de BASIC a été la première dotée de capacité graphique.

Eh oui, je me souviens encore avoir lu dans les revues de programmation de l’époque des articles signés Bill Gates expliquant un des nombreux hack en assembleur qu’il aimait inventer pour économiser de la mémoire.
Parce que voyez-vous à l’époque un développeur devait savoir calculer la place mémoire utilisée par un programme à l’octet près à partir de son source histoire d’être certain qu’il s’exécute jusque au bout !

Une époque de dingue ?
Le terme technique est plutôt « le bon vieux temps ».

Alors revenons à TRON. C’est quoi cette bête-là ?

En l’absence d’IDE et de notion de point d’arrêt et autres gadgets, la seule façon de tester un code était de l’exécuter et regarder ce qu’il affichait.
Alors BASIC a inventé une commande appelée TRON qui est l’abréviation de TRACE ON.
Dès que TRON était invoqué, l’interpréteur BASIC affichait à l’écran le code de la ligne en cours d’exécution jusqu’à ce qu’il rencontre un TROFF. Cela permettait de savoir où en était le programme.

C’est de là que vient le nom du film, encore que cette interprétation soi contestée dans Wikipedia.

TRON était un exploit technologique à l’époque car il s’agissait du premier film à utiliser des effets spéciaux numériques de façon intensive. Jurassique Park était encore loin.

Voilà un article typique des 22 février, jour de mon anniversaire !

Les Httphandler, IHttpModule et autres fichiers ASHX

Mon premier article portait sur l’écriture de IHttpmodule.

 

http://www.dotnetguru.org/articles/dossiers/etendreASPnet/EtendreASPNET.htm

 

Il s’agit là d’un outil intéressant à avoir dans votre palette, je m’en sers assez souvent, par exemple pour écrire des modules SEO.

 

J’avais évoqué la création de ASHX. A l’époque leur écriture était pénible (pas d’Intellisense, doc évanescente et aucune possibilité sérieuse de débogage). Depuis j’ai toujours en tête de compléter cet article avec les améliorations que Microsoft a apporté.

Alors le voici.

 

Les défauts d’ergonomie des ASHX ont été corrigés depuis VS 2005. Donc plutôt que de créer une page ASPX pour gérer des infos ou des dialogues purement techniques avec votre site, pensez ASHX. Pensez aussi ASHX pour gérer des fonctionnalités comme les download. C’est vraiment très simple et efficace.

 

Depuis ASP.NET 4.0, on dispose également d’une petite amélioration concernant les handler http.

Pour activer les états de session, il fallait décorer la classe avec l’interface IRequiresSessionState. Une bien curieuse façon de faire je trouve.

Les choses se sont améliorées avec la méthode HttpContexte.Current.SetSessionStateBehavior().
Rappelons que votre page Web est un handler http.

 

Voilà, blog pas bien long, mais je pense utile.

Créer un fichier en ASCII avec Visual Studio

Mon projet actuel utilise un service Windows. Pour me simplifier la vie j'ai créé un petit fichier .bat afin d'installer et lancer le service.

Ceux d'entre vous qui ont créé le fichier depuis Visual Studio ont remarqué que le fichier a un comportement bizarre une fois lancé depuis la ligne de commande.

Un message d'erreur autour d'un mystérieux caractère: ∩╗┐ apparaît.

La raison en est que par défaut Visual Studio créé des fichier en UTF-8 et place en début de fichier un caractère appelé "marque d'ordre des octets" par les experts de l'encodage.

Allez voir sur Wikipedia ce dont il s'agit:

http://fr.wikipedia.org/wiki/Marque_d%27ordre_des_octets

Jusqu'à présent je préférai créer mon fichier depuis Notepad++ et l'inclure dans la solution. Mais au détour d'un forum j'ai appris que VS est capable de créer des fichiers dans d'autres formats et c'est très simple.

  1. Créez votre fichier texte comme auparavant
  2. ouvrez le
  3. allez dans le menu File/Save as...
  4. Remarquez que le bouton Enregistrer à une petite flèche sur sa droite, cliquez dessus
  5. Une fenêtre "Advanced Save Option" s'ouvre. Choisisssez l'encodage US-ASCII

Et voilà l'affaire est réglée.

Le forum est ici:

http://stackoverflow.com/questions/854360/visual-studio-inserts-invalid-characters-in-batch-files

 

PS: ceux qui commentent ce blog ont du remarquer que votre post met du temps pour apparaître. Plusieurs semaines.

C'est parce que je suis supposé valider avant la publication et que je ne le fais pas régulièrement!!!!

Je m'en excuse d'avance.

 

pourquoi il faut plus de temps pour rejeter un mot de passe invalide que pour en accepter un correct

C’est vrai que personne ne m’a posé la question, mais c’est certainement de la timidité, vous êtes sûrement nombreux à vous demander pourquoi il faut plus de temps pour rejeter un mot de passe invalide que pour en accepter un correct ?

Je cède donc au chantage de la foule et y réponds !


J’y réponds, vite dit, je reproduits juste la réponse publiée par Raymond Chen sur son blog que j’adore: The Old New Thing [1].


La réponse courte est que valider qu’un mot de passe est incorrect n’est pas aussi simple que cela en a l’air.


Lorsque vous vous loguez la première fois, Windows place le mot de passe dans un cache spécial [2, 4] qui conserve les mots de passe des 10 derniers utilisateurs. Ainsi si vous verrouillez votre session, le déverrouillage sera instantané car Windows considère que si le mot de passe est placé dans le cache alors il est réputé validé.
Le cache permet même de se reconnecter si par hasard vous n’aviez plus de connexion à ce moment là.

Evidement vous allez me dire que même sans être connecté à mon serveur de domaine favori, je peux tout de même me reconnecter à ma machine.
Encore une fois c’est le cache de mots de passe qui agit dans les coulisses.


Je ne le savais pas, mais il est possible de le désactiver pour faire une blague désopilante à un collègue qui part faire un avant vente en clientèle par exemple [3].


Déconnez pas les gars !
La vraie raison de cette possibilité est simplement de garantir qu’il est impossible de se connecter dans un contexte de sécurité obsolète.


Revenons à notre histoire. Il y a 3 cas de figure :

  1. Le mot de passe saisi se trouve dans le cache. Super, il est donc valide et on se connecte direct, c’est rapide.
  2. Il n’est pas dans le cache : il faut interroger le contrôleur de domaine, c’est plus ou moins long.
  3. Il est dans le cache, mais la saisie est invalide. Dans ce cas on retourne immédiatement KO et c’est rapide…

    Vraiment ?


Et bien pas tout à fait. Il se pourrait parfaitement que la saisie soit en fait valide, mais que ce soit juste le hachage stocké dans le cache qui ne l’est plus parce que votre admin adoré (ou vous-même) a modifié le mot de passe depuis un autre machine. Il faut donc tester ce cas de figure là aussi en interrogeant le contrôleur de domaine.
Evidement ce n’est pas gratuit.


Il existe une autre raison plus subtile qui explique que tester un mot de passe invalide soit un peu plus long : le système ajoute automatiquement quelques secondes d’attente.
C’est bizarre vu comme cela, mais cette mesure limite le risque d’attaques avec des dictionnaires de mots de passe en augmentant de façon rapidement disproportionnée le temps moyen nécessaire pour craquer le mot de passe.

Bibliographie

[1] The Old New Thing
http://blogs.msdn.com/oldnewthing/archive/2010/03/23/9983312.aspx
[2] Sécurité des informations d'identification mises en cache dans Windows Server 2003, dans Windows XP et dans Windows 2000 (en français !!!!)
http://support.microsoft.com/kb/913485
[3] Restricting cached credentials in Windows
http://searchwinit.techtarget.com/tip/0,289483,sid1_gci968000,00.html
[4] Windows Confidential
http://technet.microsoft.com/en-us/magazine/2009.07.windowsconfidential.aspx

 

Une première mondiale!

Et oui, j'ai réalisé une véritable première mondiale. Ce fut dur, des mois de travail, l'exploit frôlé à plusieurs reprises. Mais j'y suis enfin arrivé.

Je vais devenir riche et célèbre, à côté les Turing, Gates et autres Jobs deviendront des confrères, sympas, gentils et motivés, mais des confrères.

 

Qu'ai-je réussi d'extraordinaire????

 

J'ai trouvé comment on fait un diaporama PowerPoint SANS fond sonore. Si si, rien que ça.

 

La technique est complexe je l'avoue et d'ailleurs j'organise des formations si vous avez du mal.

 

Voici comment faire:

  1. Vous n'allez pas dans l'onglet Insertion
  2. Si vous devez aller dans cet onglet pour des raisons professionnelles. Vous ne cliquez pas sur l'icône SON qui est tout à droite.

 

Je n'explique pas encore bien comment cela arrive, mais peut être les MVP qui me lisent ont des infos qui ne soient pas NDA. Et bien cela n'insère AUCUN fond sonore.

 

J'en voit dont la machoire vient de tomber à terre.

 

Lancer une application WPF si l'on a plusieurs écrans

Je travaille en général avec deux écrans et il m'arrive donc fréquemment de déplacer d'un écran à l'autre l'application que je viens de lancer depuis VS. Opération que j'ai toujours trouvée non pas difficile, mais pénible à faire continuellement.

 

J'ai découvert cette semaine par hasard qu'en fait c'est relativement inutile puisque l'algorithme de démarrage de VS est plus intelligent que moi.

Au lancement de l'application il "regarde" dans quelle fenêtre se trouve le curseur souris et c'est dans cette fenêtre qu'il lance l'application.

J'ai fais des essais, cela ne marche bien qu'avec les applicatons WPF.

Et la cerise sur le gâteau: le comportement est en fait un standard de l'application. Lancez votre EXE indépendemment de VS et ça marche!!!!

 

WPF : Débogage des bindings [ajout]

Les bindings c’est réellement une des forces de WPF. Mais parfois ils peuvent ne pas fonctionner ou bien ne pas charger le résultat attendu.

Comment faire pour obtenir des informations de plus bas niveau que de simplement constater que cela ne marche pas ?

Dans le cas où une exception est levée, le plus simple est de faire View/Output et regarder ce qui se passe dans la console de sortie. On va par exemple trouver ceci :

System.Windows.Data Error: 4 : Cannot find source for binding with reference 'ElementName=zoomSlider'. BindingExpression:Path=Value; DataItem=null; target element is 'ScaleTransform' (HashCode=34917486); target property is 'ScaleX' (type 'Double')

Parfois ce n’est pas suffisant ou bien le binding ne lève pas d’exception, mais ne se lie pas à la source que l’on pense.

Ouvrez alors votre code XAML et déclarez l’espace de noms suivant :

 

xmlns:diag="clr-namespace:System.Diagnostics;assembly=WindowsBase" 

 

Vous pouvez lui attribuer le tagkey que vous souhaitez, j’ai choisis diag, mais zjergtziuyaziuefgapiugzpiufgzffiudgf fait aussi l’affaire si cela vous paraît plus clair !

L’étape suivante consiste à repérer le binding que l’on souhaite auditer, dans notre cas :

 

<ScaleTransform ScaleX="{Binding ElementName=zoomSlider,

                Path=Value}"

                ScaleY="{Binding ElementName=zoomSlider,

                Path=Value}" />

</TabControl.LayoutTransform>

 

On effectue la modification suivante :

<ScaleTransform ScaleX="{Binding ElementName=zoomSlider,

                         Path=Value,                             diag:PresentationTraceSources.TraceLevel=High}"

                ScaleY="{Binding ElementName=zoomSlider,

                Path=Value}"/>

</TabControl.LayoutTransform>

 

On relance et consulte à nouveau la console de sortie. On retrouve évidemment les deux messages précédents, mais avant :

 

System.Windows.Data Warning: 63 : BindingExpression (hash=42815147): Resolving source

System.Windows.Data Warning: 66 : BindingExpression (hash=42815147): Found data context element: <null> (OK)

System.Windows.Data Warning: 70 :     Lookup name zoomSlider:  queried DockPanel (hash=64981649)

System.Windows.Data Warning: 63 : BindingExpression (hash=42815147): Resolving source

System.Windows.Data Warning: 66 : BindingExpression (hash=42815147): Found data context element: <null> (OK)

System.Windows.Data Warning: 70 :     Lookup name zoomSlider:  queried DockPanel (hash=64981649)

System.Windows.Data Warning: 63 : BindingExpression (hash=42815147): Resolving source  (last chance)

System.Windows.Data Warning: 66 : BindingExpression (hash=42815147): Found data context element: <null> (OK)

System.Windows.Data Warning: 70 :     Lookup name zoomSlider:  queried DockPanel (hash=64981649)

 

Le log me dit que WPF va chercher le DataContext dans un DocPanel. Or mon binding ne se trouve pas dans un DockPanel ?

Que se passe-t-il ?

Je fais une recherche dans les différents XAML et découvre un ScaleTransform oublié dans un UserControl. A l'origine c'est lui qui contenait le Slider, mais j'ai déplacé celui-ci et oublié le ScaleTransform.

Une fois celui-ci supprimé, tout est rentré dans l'ordre !

Moralité : utilisez des ressources plutôt qu'une déclaration directe dans les templates !!!!

Dans un de ses blogs, Thomas Lebrun propose un script de validation des bindings :

http://blogs.developpeur.org/tom/archive/2008/11/06/wpf-valider-les-binding-wpf.aspx

Honnêtement je ne suis pas arrivé à comprendre ce qu'il valide au juste ! Si quelqu'un a une idée ?

 

AJOUT: je viens de trouver ce blog de Bea Stollnitz qui aborde la même question, mais avec des méthodes supplémentaires:

http://bea.stollnitz.com/blog/?p=52

 

 

les chaînes de connexion

Un article sans autre prétention que rassembler un certain nombre d’informations en général assez éparses sur les chaînes de connexion.
Il n’y a pas d’ordre particulier, juste des chapitres qui se suivent.


un peu d'orthographe

• En anglais on écrit : connection
• Par contre en français : connexion, avec un x.

Allez donc relire votre doc et vos commentaires…


Sauver une chaîne dans une application .net

Examinons différents conteneurs, sans prétendre être exhaustif.

Le code

Cette possibilité est assez rare car dans la majorité des cas il s’agit plutôt d’une faute de codage. Avant de l’adopter, évaluez en les avantages et les inconvénients avec soin.

Le fichier de configuration

Depuis .NET 2.0 est apparue la structure <connectionStrings>.
Par exemple :

<connectionStrings>
  <add name="nomChaineConnexion" connectionString="XXXXXXXX"/>
</connectionStrings>

name: Nom du conteneur
connectionString: Chaîne de connexion

Côté code, la récupération de la chaîne se fait ainsi :

ConnectionStringSettings chaine = ConfigurationManager.ConnectionStrings["nomChaineConnexion"];

Le nom du conteneur peut faire l’objet d’une entrée dans la section <appSettings > que l’on peut alors relire en interrogeant le dictionnaire ConfigurationManager.AppSettings.

La classe ConnectionStringSettings propose diverses propriétés, mais celle qui vous intéressera le plus est ConnectionString qui est du type String et retourne biens sûr notre chaîne.

Qu'en est t'il côté sécurité?

Par défaut ASP.NET est configuré pour ne jamais télécharger le fichier web.config. On n’a donc pas de risque de le voir apparaître en navigant sur le site.

Le réglage est fait dans le fichier web.config par défaut situé dans :

%SystemRoot%\ Microsoft.NET\Framework\v2.0.50727\CONFIG

Ce fichier est apparu avec .NET 2.0, le paramétrage se trouve dans machine.config dans les versions antérieures de .NET.

Ce dernier fichier se trouvant hors répertoire virtuel, il est donc considéré comme raisonnablement sûr pour une application Web, d’autant plus que des ACL sont posées sur ce fichier lors de l’installation de .NET.

La protection contre les attaques extérieures est donc sûre.

On peut encore augmenter la sécurité en cryptant la chaîne de connexion à l’aide de l’outil aspnet_regiis.exe livré avec ASP.NET.
Faisons ensemble une petite démonstration, mais sinon reportez vous à la référence [6] pour plus de détails.
Par exemple (mais d’autres syntaxes existent):

aspnet_regiis -pef "connectionStrings" "C:\Projets\...."

Notez que l’un des paramètres est la section que l’on souhaite crypter, on n’est donc pas limité aux chaînes de connexion.
Le résultat ressemble à ceci :

<connectionStrings configProtectionProvider="RsaProtectedConfigurationProvider">
    <EncryptedData Type="http://www.w3.org/2001/04/xmlenc#Element"
      xmlns="http://www.w3.org/2001/04/xmlenc#">
      <EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#tripledes-cbc" />
      <KeyInfo xmlns="http://www.w3.org/2000/09/xmldsig#">
        <EncryptedKey xmlns="http://www.w3.org/2001/04/xmlenc#">
          <EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#rsa-1_5" />
          <KeyInfo xmlns="http://www.w3.org/2000/09/xmldsig#">
            <KeyName>Rsa Key</KeyName>
          </KeyInfo>
          <CipherData>
            <CipherValue>BkzayktAAs….BDkjEuG3ZVRs/M=</CipherValue>
          </CipherData>
        </EncryptedKey>
      </KeyInfo>
      <CipherData>
        <CipherValue>UFds9Ssuc6+gHRwt3G…bWqQS31ha</CipherValue>
      </CipherData>
    </EncryptedData>
  </connectionStrings>

Notez plusieurs choses importantes :

• C’est transparent pour votre code
• L’opération est réversible
• Le résultat du cryptage est, par défaut, spécifique à la machine sur lequel il a été effectué, mais il est possible de mettre en place un conteneur RSA particulier comme expliqué en [7]

A ma connaissance cet outil ne s’applique qu’au fichier web .config, du moins je ne suis pas parvenu à l’appliquer à un fichier de configuration d’une application console. Si quelqu’un sait faire sans avoir à renommer le fichier…

Fichier udl (universal data link)

Pour créer un fichier udl :
• Naviguez jusqu’au répertoire où vous souhaitez le déposer
• Créez un fichier texte avec l’extension .udl
• Double cliquez dessus. Une fenêtre suivante s’ouvre
• Remplissez la boite de dialogue.

Une fois terminé, le fichier contiendra la chaîne de connexion complète.

Au sujet du choix du fournisseur, notez que seul OLE DB est supporté par ADO.NET avec les fichiers udl. Mais c’est un moyen que j’aime bien utiliser pour construire une chaîne de connexion.

Côté code maintenant. Disons que votre fichier se nomme monFichierAMoi.udl.

OleDbConnection myConnection = new OleDbConnection("File Name = c:\\monFichierAMoi.udl");

Vous trouverez plus d’infos en [1] et [2].

Côté sécurité?

Ne surtout pas placer le fichier udl dans le répertoire virtuel car il ne sera pas protégé du téléchargement. Pour éviter de possibles bugs de canonicalisation [3], [4], il est même préférable de le disposer dans un volume logique différent de celui du site.

Comme tout fichier critique, il doit être protégé par des ACL adaptés. A ce titre l’accès au fichier udl peut être mieux protégé que celui de web.config car on peut le rendre accessible à nettement moins de comptes. Il y aura au moins MachineName\ASPNET qui n’a besoin que d’un accès en lecture.

Dans la mesure où le fichier udl peut être modifié à l’insu de l’application, celui-ci n’est pas mis en cache, mais lu à chaque appel. Cela peut avoir un impact sur les performances

Il n’est pas possible d’encrypter les fichiers udl.

Fichier personnalisé

Cette option n’est intéressante que si le fichier est placé en dehors du répertoire virtuel pour des raisons de sécurité.
Il doit être protégé par des ACL restrictives et idéalement placé dans un volume distinct de l’application.

Le registre

Pour que l’application ASP puisse y accéder, vous devrez placer la chaîne de connexion dans HKLM. Vous devrez également :
• Protéger la clef avec un ACL
• L’encrypter (voir [5] pour une procédure)

Créer un DSN

Un DSN (Data Source Name) est simplement un fichier avec toutes les informations pour identifier une source de données accessible via ODBC.

Il existe deux types de DSN :
1. Les DSN utilisateur qui ne sont visible que d’un utilisateur sur la machine où le DSN est déployé
2. Les DSN système qui sont accessibles à tous les utilisateurs d’un même système ou connectés au système

Comment faire ?

Il faut rechercher dans le panneau de contrôle le lien vers un utilitaire appelé « ODBC Manager » ou bien « Source de données ODBC ». On le lance et on obtient une fenêtre avec différents onglets. On sélectionne "Source de données système".

Vous trouverez en [14] un tutoriel très bien fait qui vous expliquera la suite.

Côté code rien de très compliqué, vous ouvrez une connexion avec OdbcConnection et créez une chaîne de connexion indiquant juste le nom du DSN avec le mot clef DSN.


Utiliser une chaîne fournie par l'utilisateur

Il peut arriver que la chaîne doive être construite dynamiquement ou bien construite à partir d’informations saisies par l’utilisateur. Dans ce dernier cas, c’est une assez mauvaise idée de le laisser saisir la chaîne elle-même.

Il est plutôt recommandé d’utiliser une des implémentations de la classe DBConnectionStringBuilder pour construire de façon sécurisée une chaîne de connexion. Par exemple pour un fournisseur SQL Server:

SqlConnectionStringBuilder builder = new SqlConnectionStringBuilder();
builder.IntegratedSecurity = false;

string maChaine = builder.ConnectionString;

En standard on dispose des implémentations suivantes:
1. SqlConnexionStringBuilder
2. OracleConnexionStringBuilder
3. OdbcConnexionStringBuilder
4. OleDbConnexionStringBuilder

La classe de base DBConnexionStringBuilder vous permettra de fournir votre implémentation pour d’autres cas.
Cette classe est également intéressante si vous souhaitez analyser le contenu d’une chaîne de connexion.


Comment construire une chaîne de connexion

On a déjà vu trois procédés :
1. L’écrire directement à la main
2. Construire un fichier udl et y lire la chaîne résultante
3. Utiliser une classe dérivée de DBConnexionStringBuilder

On peut aussi trouver des exemples de chaînes de connexion toutes faites ici :

http://www.connectionstrings.com

Mais aussi ici :

http://connectionstringexamples.com

Vous y trouverez des centaines d’exemples de chaînes de connexion pour de nombreuses bases de données.
Retenez au moins ceci :

• Les noms ne sont pas sensibles à la casse (mais les valeurs éventuellement, exemple les mots de passe)
• Les valeurs ne sont pas typées
• Si une même paire nom=valeur apparaît plusieurs fois, seule la dernière sera prise en compte
• Mais : si le mot clef PROVIDER apparaît plusieurs fois, c’est le premier qui est pris en compte !!!!
• Les valeurs true ou false peuvent aussi s’écrire yes ou no

Vous trouverez plus d’informations ici :

http://www.connectionstrings.com/Articles/Show/important-rules-for-connection-strings


On trouve quoi dans une chaîne de connexion

A ma connaissance il n’existe pas de norme particulière, chaque conteneur de données y place ce qu’il souhaite. Mais on retrouve malgré tout une certaine systématique.

L’article qui suit liste la plupart des paramètres que l’on peut rencontrer dans une chaîne de connexion avec leurs valeurs par défaut :

http://www.connectionstrings.com/Articles/Show/all-sql-server-connection-string-keywords

Comme vous le constatez, il y a beaucoup de synonyme.

INITIAL CATOLOG

On rencontre aussi DATABASE. Désigne la base de données associée à la connexion.

DATA SOURCE

Ou aussi SERVER et plein d’autres d’ailleurs !

Nom ou adresse réseau de l’instance de la base de données à laquelle se connecter.

Ce que j’ai appris récemment est que l’on peut y passer le nom du protocole de communication utilisé :

DATASOURCE=np :



Pour les canaux nommés (named pipe).

Mais aussi un numéro de port si celui par défaut ne convient pas :

DATASOURCE=tcp :,1234

PERSIST SECURITY INFO

Ce paramètre prend true ou false. La valeur par défaut est false.

D’après la documentation cette valeur indique que les informations de sécurité telles le mot de passe, sont retournées dans le cadre de la connexion si celle-ci est ouverte ou a été ouverte à un moment donné.

Personnellement ce genre de définition ne me semble pas bien claire. Je ne vois pas bien ce que cela impacte pour moi et encore moins dans quels cas je dois choisir une valeur plutôt qu’une autre.

Comme souvent, un petit essai clarifie les choses :
using (SqlConnection connnexion = new SqlConnection("Persist Security Info=false;Data Source=.;Initial Catalog=Northwind;Uid=IsgUser;Pwd=ISGuser!"))
{
    string avant = connnexion.ConnectionString;
    connnexion.Open();
    string apres = connnexion.ConnectionString;

    using (SqlCommand commande = connnexion.CreateCommand())
    {
        commande.CommandText = "select * from Customers";
        using (SqlDataReader reader = commande.ExecuteReader(CommandBehavior.CloseConnection))
        {
            int ordinal = reader.GetOrdinal("CompanyName");

            while (reader.Read())
            {
                Console.WriteLine(reader.GetString(ordinal));
            }
        }
    }
}

Le code interroge la base NorthWind et lit le contenu d’une table. Notez la valeur explicitement mise à false du paramètre PERSIST SECURITY INFO dans la chaîne de connexion.

Nous ne nous intéressons qu’aux deux variables avant et apres que l’on visualise ainsi :

Effectivement ce n’est pas bien difficile de comprendre PERSIST SECURITY INFO.

La chaîne de connexion complète, avec les credentials, est nécessaire pour créer le pool de connexion et se connecter à la source de données (ce que fait la méthode Open).

Par contre une fois la connexion établie, les parties sensibles du credential sont supprimées. Il devient possible de passer la connexion à une application qui n’est pas une application de confiance puisqu’elle ne pourra pas réutiliser la valeur de la propriété ConnectionString pour récupérer vos credentials.

La conclusion de tout cela est que l’on n’a que rarement l’occasion de mettre true et si vous souhaitez loguer la chaîne de connexion, mieux vaut donc apres que avant.

INTEGRATED SECURITY

Egalement appelée « trusted connection ».

Voici les valeurs possibles de cette propriété :

• SSPI (Security Support Provided Interface)
• true
• false qui est aussi la valeur par défaut

True indique que l’on se connecte avec les credentials fournis par le compte Windows courant.

False indique que la chaîne de connexion doit fournir des credentials (paramètres USER ID et PWD).

Et SSPI me direz vous ?

J’ai longtemps cru que cette valeur était synonyme de true, mais en fait ce n’est pas toujours le cas comme je l’ai découvert en lisant un article du blog « La voie du code« [9].

SSPI est une API Microsoft destinée à effectuer un certain nombre d’opérations liées à la sécurité comme par exemple l’authentification [10]. SSPI permet donc de disposer d’une interface commune pour accéder à des protocoles tels Kerberos ou NTLM.

Par conséquent l’option SSPI ne fonctionnera que si votre protocole réseau utilise SSPI.
Parmi les exceptions se trouvent justement les canaux nommés (named pipes). Dans ce cas il faut donner true comme valeur d’INTEGRATED SECURITY.

Si vous êtes amenés à vous servir des canaux nommés dans une application ASP.NET lisez l’article en [11].
Si vous souhaitez en savoir plus sur les protocoles réseaux accessibles à SQL lisez [12] et [13].


SqlBulkCopy

Cette classe apparue avec .NET 2.0 permet de charger en bloc une table SQL Server avec des données provenant d’une autre source, pas nécessairement SQL Server. Il suffit de pouvoir les lire dans un DataSet ou bien avec une instance d’IDataReader.

Cette classe fonctionne un peu comme l’utilitaire en ligne bcp fournit avec SQL Server.
Les performances sont bien meilleures qu’avec des INSERT, mais on perd les logs.


Spécial sql express

SQL Express a diverses particularités et notamment la notion d’interface utilisateur (user instance) utilisée par les projets se servant de base de données locale. User instance est un process spécifique à SQL Express qui peut être démarré par votre application au cours de son exécution. Du point de vue du code, tout se passe comme si vous disposiez d’une base de données embarquée.

Voici deux articles qui vous diront tout à ce sujet :

http://msdn.microsoft.com/fr-fr/library/bb264564(SQL.90).aspx

Et en anglais un blog vraiment intéressant où vous apprendrez à vous connecter à une user instance via management studio:

http://blogs.msdn.com/sqlexpress/archive/2006/11/22/connecting-to-sql-express-user-instances-in-management-studio.aspx


Bibliographie

[1] Comment faire pour utiliser des fichiers de liaison de données avec l'objet OleDbConnection dans Visual C# .NET
http://support.microsoft.com/kb/308426
[2] ADO.NET and UDL
http://www.codeproject.com/KB/database/ADONETUDL.aspx[3] Les attaques par canonicalisation 1/2
http://www.dotnetguru2.org/amethyste/index.php?p=103&more=1&c=1&tb=1&pb=1
[4] Les attaques par canonicalisation 2/2
http://www.dotnetguru2.org/amethyste/index.php?p=104&more=1&c=1&tb=1&pb=1
[5] Procédure : Stocker une chaîne de connexion cryptée dans le Registre
http://msdn.microsoft.com/fr-fr/library/aa302406.aspx
[6] Outil ASP.NET IIS Registration (Aspnet_regiis.exe)
http://msdn.microsoft.com/fr-fr/library/k6h9cz8h(VS.80).aspx
[7] Crypter une section du fichier de configuration avec aspnet_regiis
http://blogs.developpeur.org/sanzio/archive/2008/04/14/crypter-une-section-du-fichier-de-configuration-avec-aspnet-regiis.aspx
[8] Secure ADO.NET connections
http://msdn.microsoft.com/en-us/library/89211k9b(VS.71).aspx
[9] Le vrai sens de Integrated Security=SSPI
http://www.doanvanbien.com/voieducode/archives/2006/09/13/21
[10] SSPI Reference sur MSDN
http://msdn.microsoft.com/en-us/library/aa380493(VS.85).aspx
[11] Les canaux nommés ne fonctionnent pas quand le processus de traitement s'exécute sous un compte ASPNET
http://support.microsoft.com/Default.aspx?id=315159
[12] [SQL] Force the protocol (TCP, Named Pipes, etc.) in your connection string
http://weblogs.asp.net/jgalloway/archive/2007/02/24/sql-force-the-protocol-tcp-named-pipes-etc-in-your-connection-string.aspx
[13] Choix d’un protocol réseau
http://msdn.microsoft.com/fr-fr/library/ms187892.aspx
[14] Création d’un DSN ODBC
http://www.commentcamarche.net/contents/odbc/odbcdsn.php3

Accélérer MSDN

Un petit truc amusant que m’a apprit Jon Galloway dans son blog [1].

Prenons par exemple cette url qui pointe vers une des pages de MSDN :

http://msdn.microsoft.com/en-us/library/system.object.aspx

Elle se charge sous vos yeux, c’est relativement lent car elle charge des composants bien sophistiqués.
Transformez là ainsi :

http://msdn.microsoft.com/en-us/library/system.object(loband).aspx

Cette fois la page a une autre allure et se charge plus rapidement.

Certaines pages disposent déjà d’un tag, par exemple :

http://msdn.microsoft.com/en-us/library/cc189009(VS.95).aspx)

Dans ce cas on peut la transformer ainsi :

http://msdn.microsoft.com/en-us/library/cc189009(VS.95,loband).aspx)

Le tag loband est placé dans un cookie et maintenant toutes vos pages MSDN se chargeront en mode loband.
L’opération est toutefois réversible si vous cliquez ici en haut à droite de l’écran:


Tant que l’on y est, rappelons quelques hacks utiles.
Regardez cette url :

http://msdn.microsoft.com/en-us/library/system.object.aspx

Pour avoir une version en français il suffit de remplace le code régional us par :

http://msdn.microsoft.com/fr-fr/library/system.object.aspx

Ce n’est pas tout. Le nom de la page est le suivant :

System.object.aspx

C’est simplement qu’il s’agit de la page d’aide de la classe System.Object.
Je vous laisse deviner la conclusion…

Bibliographie

[1] MSDN Low Bandwidth Bookmarklet
http://weblogs.asp.net/jgalloway/archive/2008/08/30/msdn-low-bandwidth-bookmarklet.aspx

Sortir du moule avec le pattern provider

Actuellement je travaille sur un site Web capable de s’adapter à plusieurs marques et pour différents pays.
L’idée générale est de construire un moule auquel tout le monde devra s’adapter.

Ce n’est pas toujours possible, en particulier la difficile question de certaines règles métiers qui peuvent différer fortement.
Par exemple les prix, promotion et autres cadeaux clients répondent à des logiques différentes selon la marque.

On a donc besoin par moment de sortir localement du moule et exécuter un code ou un autre selon le contexte. Comment s’y prendre concrètement ?

Si on ne parlait que de quelques lignes on pourrait envisager de placer une ou deux clauses if et tester par exemple le nom de la marque. Evidemment si une nouvelle marque vient s’ajouter, on devra prendre le risque de modifier un code qui marche.

Ce blog est publié sur un site qui fait depuis toujours la promotion de l’architecture de code alors on va essayer de faire mieux avec un patron de conception que j’aime bien : le provider.

L’idée générale n’est pas très révolutionnaire. On a (au moins) trois étapes à implémenter :

1. On écrit une interface qui définit la liste des méthodes et propriétés communes. Lorsque je parle d’interface, il s’agit aussi de classe abstraite. A vous de voir ce qui est utile dans votre cas
2. On implémente cette interface autant de fois que nécessaire (une implémentation par marque a priori)
3. On isole la logique d’instanciation de la classe réelle selon le contexte dans une classe factory qui retourne une instance de notre interface.
4. Puisque l’on est ambitieux, on voudrait que la factory soit paramétrable de façon à ne pas avoir besoin de la modifier si une nouvelle marque vient s’ajouter

Le provider est omniprésent dans .Net, il n’est donc pas étonnant que Microsoft nous fournissent une plomberie pour l’implémenter facilement. Découvrons la ensemble.

On commence par l’interface qui doit avoir une dépendance technique avec la classe ProviderBase [1].

public abstract class PricesProvider : ProviderBase

La suite du code définit les méthodes, propriétés… dont nous aurons besoin. Par exemple :

/// <summary>
/// Obtient le meilleur prix d'un article donné compte tenu des réductions auxquelles le client est eligible
/// </summary>
/// <param name="userContext">Instance de <see cref="IPrincipal"/> détenant le profil du client</param>
/// <param name="idProduit">Id du produit</param>
/// <returns>Meilleurs prix proposé</returns>
public abstract double GetBestPrice(IPrincipal userContext, string idProduit);

Je vous conseille vivement d’adopter une cohérence dans la signature des méthodes. Par exemple dans notre cas elles dépendent toutes du profil client que nous chargeons dans l’IPrincipal en cours (on a écrit un principal personnalisé). Celui-ci est par convention le premier paramètre de la ligne et porte systématiquement le même nom et les autres paramètres sont toujours positionnés dans le même ordre.

Croyez moi, c’est très pratique à l’usage et peut à l’occasion éviter quelques bogues de copier/coller.

Il ne vous reste plus qu’à implémenter votre logique métier pour chaque marque :

public sealed class ContosoPricesProvider : PricesProvider
{
Random rnd = new Random(DateTime.Now.Millisecond);

public double GetBestPrice(IPrincipal userContext, string idProduit)
{
    return 500 * rnd.NextDouble();
}
}

Note : ce code n’est pas contractuel !

Implémentons la factory. Le corps de la classe est le suivant :

public static class PricesFactory
{
// le code ici
}

Avant de procéder à son implémentation des étapes intermédiaires nous attendent.
Tout d’abord une collection de fournisseurs nous rendra quelques services :

public sealed class PricesProviderCollection : ProviderCollection
{
    new public PricesProvider this[string name]
    {
        [DebuggerStepThrough]
        get
        {
            return (PricesProvider)base[name];
        }
    }

    public override void Add(ProviderBase provider)
    {
        if (provider == null)
        {
            throw new ArgumentNullException(Ressources.PricesProviderCollection_ProviderNull);
        }

        if (!(provider is PricesProvider))
        {
            throw new ArgumentException("Le type du provider doit être PricesProvider");
        }

        base.Add(provider);
    }
}

Il s’agit simplement de typer fortement ProviderCollection qui ne semble pas exister en version générique. Rien d’obligatoire, mais conseillé tout de même.

Nous souhaiterions déclarer dans le fichier web.config le provider qui sera utilisé. Pour cela on doit écrire une classe de configuration. Rien de plus simple :

public sealed class PricesProviderConfiguration : ConfigurationSection
{
    [ConfigurationProperty("providers")]
    public ProviderSettingsCollection Providers
    {
        get
        {
            return (ProviderSettingsCollection)base["providers"];
        }
    }

    [ConfigurationProperty("default")]
    public string DefaultProviderName
    {
        [DebuggerStepThrough]
        get
        {
            return base["default"] as string;
        }

        [DebuggerStepThrough]
        set
        {
            base["default"] = value;
        }
    }
}

On peut ajouter diverses fioritures selon ce que vous souhaitez faire, mais vous voyez l’idée générale qui est de pouvoir ajouter dans le fichier de configuration quelque chose comme ceci :

<PricesConfigSection default="ContosoPricesProvider">
  <providers>
    <add name="ContosoPricesProvider" type=" PriceModule.ContosoPricesProvider, Business" />
  </providers>
</PricesConfigSection>

Dans la section <providers> vous ajoutez autant de déclaration de classe réelle que nécessaire, et l’attribut default permet de sélectionner celle que le provider devra charger.

Une petite formalité toutefois, il faut aussi ajouter une déclaration similaire à celle-ci :

<section name="PricesConfigSection" type=" PriceModule.PricesProviderConfiguration, Business" />

Pour que l’analyseur du fichier web.config sache comment traiter la section ajoutée.
Revenons donc à notre factory.
Le constructeur (forcément static) est très simple :

static PricesFactory()
{
    Initialize();
}

La méthode Initialize va simplement charger la collection de fournisseur trouvée dans le fichier de configuration. On implémente un patron singleton.

private static void Initialize()
    {
        if (IsInitialized)
        {
            return;
        }

        // début de l'initialisation
        lock (InitializationLock)
        {
            // pattern du double-checkin
            if (IsInitialized)
            {
                return;
            }

            PricesProviderConfiguration configSection = (PricesProviderConfiguration)ConfigurationManager.GetSection("PricesConfigSection");

            if (configSection == null)
            {
                throw new …
            }

            _Providers = new PricesProviderCollection();
            ProvidersHelper.InstantiateProviders(configSection.Providers, _Providers, typeof(PricesProvider));
            _ProviderSettings = configSection.Providers;

            if (_Providers[configSection.DefaultProviderName] == null)
            {
                throw new …
            }
            _Default = _Providers[configSection.DefaultProviderName];

            IsInitialized = true;
        }
    }

    /// <summary>
    /// <b>false</b> indique que la classe n'a pas été initialisée
    /// </summary>
    private static bool IsInitialized;
    /// <summary>
    /// Variable utilisée pour poser un verrou
    /// </summary>
    private static object InitializationLock = new object();

Notez l’utilisation des classes ConfigurationManager et ProvidersHelper qui font l’essentiel du travail. Donc au total un code relativement simple.

Ce n’est pas tout, on doit encore déclarer la propriété suivante qui sera alimentée par la méthode que nous venons de voir :

[DebuggerBrowsable(DebuggerBrowsableState.Never)]
private static PricesProvider _Default;

public static PricesProvider Default
{
    [DebuggerStepThrough]
    get
    {
        return _Default;
    }
}

Elle remonte le provider par défaut déclaré dans le fichier de configuration. Notez bien que la propriété est statique.

On peut agrémenter la classe en rendant public les collections ProvidersCollection ou ProviderSettingsCollection.

Bibliographie

[1] Aide en ligne sur ProviderBase
http://msdn.microsoft.com/fr-fr/library/system.configuration.provider.providerbase.aspx

Des sons mystérieux

Pour mon anniversaire je ne vais pas parler informatique, encore qu’aucunes de ces informations ne seraient connues sans, mais de sujets plus légers : les mystères de la nature !!!!

En permanence des organismes de recherche ou militaires écoutent les sons entendus au fond des océans. La plupart d’entre eux sont catalogué et identifiés. La plupart parce que certains sont classés secret défense.

Par exemple tant qu’un sous marin de notre flotte nucléaire est encore en activité, le détail de ses pérégrinations sous les mers est tenu secret. C’est au cas où une puissance étrangère aurait enregistré son passage, avec des recoupements elle pourrait parvenir à l’identifier et donc le repérer.

Mais durant l’été 1997 le réseau hydrophone de l’US Navy a entendu ceci autour de 50° S 100° W (côte sud-ouest de l’Amérique du Sud) :

Ce son est appelé le bloop dans la littérature. Il ne s’est jamais reproduit depuis cet été là.
Alors c’est quoi ?

• Il ressemble aux bruits émis par une baleine. Mais si c’est le cas elle serait encore plus gigantesque que la baleine bleue et il y a fort à parier qu’elle aurait été aperçue depuis longtemps.
• On ne pense pas que ce soit un calmar géant car aucune des espèces connue n’a les dispositions anatomiques pour produire un son de cette nature.
• Un sous marins ? On ne voit pas très bien comment un son aussi intense pourrait être produit.

Bref on n’en sait rien.

Le plus étonnant est ailleurs. Souvenez-vous des coordonnées. Elles sont étonnamment proches de celle-ci : 48°50S 123°20W.

Ce point est appelé par les géographes le point Nemo. Il s’agit du point le plus éloignés de toutes les terres.

nemo

Par une étrange coïncidence, ce point est également proche de la ville fictive de R’lyeh imaginée par H. P. Lovecraft. Dans sa nouvelle L’Appel de Cthulhu, Lovecraft avait situé R’lyeh à 47°9′S 126°43′W dans l’Océan Pacifique sud. Dans la mythologie lovecraftienne, le grand ancien Cthulhu était enfermé dans cette cité mythique.

Des sons mystérieux il y en a bien d’autres.

Par exemple le slow-down[5] enregistré la même année (19 mai 1997) dans l’océan pacifique et jamais réentendu.

Certains signaux sont extraterrestres. Ainsi le signal Wow [4] capté le 15 août 1977 par un radio télescope pendant 72 secondes dans le cadre du projet SETI. On ne sait pas du tout ce que c’est, inutile de dire que son côté extraterrestre a échauffé bien des esprits fertiles…

Bibliographie

[1] Le bloob sur l’irremplaçable Wikipédia:
http://fr.wikipedia.org/wiki/Bloop

[2] Mais il a aussi sa home page :
http://www.bloopwatch.org/thebloop.html

[3] Le point Nemo
http://fr.wikipedia.org/wiki/Point_N%C3%A9mo

[4] Le Wow
http://fr.wikipedia.org/wiki/Signal_Wow!

[5] Le slow down
http://fr.wikipedia.org/wiki/Slow_Down

Optimiser l'indexation de votre site avec un nouveau tag

Les sites marchands ont tous besoin d'une stratégie d'indexation. En clair être reconnus par Google et ses concurrents principaux: Yahoo et Live.

Un problème récurrent est qu'il arrive que deux url différentes aboutissent à la même page (présence ou non d'ordre de tri par exemple) sans que cela soit significatif du point de vue de l'indexation.

Saluons donc l'initiative coinjointe de Google, Yahoo et Live de prendre en charge un nouveau tag: canonical pour justement nous permettre de déclarer la page étant la plus significative à indexer et d'y consolider tous les liens différents qui pointent vers elle.

Les explication complètes (en français!) sont ici sur le site Moteurs News [1].
Et si vous souhaitez connaître les détails d'implémentation liés à chaque société: [2], [3] et [4]

Le hasard fait bien les choses, c'est justement au moment où le client nous demandait s'il était possible de résoudre ce problème que l'annonce a été faite...

Bibliographie

[1] URL canonique, nouveau tag de Google Yahoo! et Microsoft

http://www.moteurs-news.com/blog/index.php/2009/02/13/202-url-canonique-nouveau-tag-de-google-yahoo-et-microsoft

[2] Annonce Google:

http://googlewebmastercentral.blogspot.com/2009/02/specify-your-canonical.html

[3] Annonce Yahoo:
http://ysearchblog.com/2009/02/12/fighting-duplication-adding-more-arrows-to-your-quiver/

[4] Annonce Microsoft:

http://blogs.msdn.com/webmaster/archive/2009/02/12/partnering-to-help-solve-duplicate-content-issues.aspx

Lab sur le bloc d'application Validation

Le groupe Patterns & Practices a pour objectif de proposer des règles de conception et de développement pour tirer le meilleur profit de la plateforme Microsoft. Fréquenter régulièrement leur site est donc obligatoire pour tout architecte ou développeur sérieux [1].

Vous y trouvez beaucoup de matériaux : des livres en ligne, des framework prêt à l’emploi (appelés Application Block), des vidéos, des tutoriels, des labs…

Mais aussi des gourous qui ne s’appellent pas MVP, mais Champions dans la terminologie du groupe !

L’accent est placé très fort sur la documentation et l’aide à la formation. Si je n’avais qu’un regret à formuler et que tout est en anglais, mais cela ne doit absolument pas vous décourager.

Si vous souhaitez en savoir plus sur ce groupe et leurs méthodes de travail, rendez vous ici [2].

La dernière livraison des Entreprises Library (Entlib) porte le numéro de version 4.1. Ceux qui sont allés au TechEd on pu suivre une conférence de Grigori Melnik sur les nouveautés qu’elle apporte avec une amusante application de régulation des feux rouges à Seattle.

En janvier Microsoft a publié un très intéressant lab sur l’application block Validation [3].
Comme toujours, la finition des AB est très soignée et le lab couvre pratiquement toutes ses possibilités:

• Un jeu d’attributs à poser sur les membres d’une classe pour définir les contraintes auxquelles ils doivent répondre:
être un entier, non null, une chaîne d’au moins 4 caractères, une valeur parmi une liste donnée…
• Le framework offre un support de validation des éléments d’une collection
• Plutôt que de jouer avec des attributs, on peut paramétrer la stratégie de validation depuis le fichier de configuration soit à la main, soit à l’aide d’un assistant
• Support de plusieurs stratégies de validation à travers l’application de règles:
Certains membres peuvent être obligatoires dans un contexte et pas un autre par exemple
• Déclaration de méthodes d’auto-validation lancées automatiquement au moment de la validation
• Support de WCF
• Des composants pour les Winforms et ASP.NET
• Modèle de données extensible, on n’est pas limité aux stratégies de validation fournies par le framework

Bref du très beau travail.

Bibliographie

[1] Page d’accueil du groupe Patterns & Practices
http://msdn.microsoft.com/fr-fr/practices/default(en-us).aspx
[2] Qui sont t’ils ?
http://msdn.microsoft.com/fr-fr/practices/bb190357(en-us).aspx
[3] Lab du bloc d’application Validation :
http://www.microsoft.com/downloads/details.aspx?FamilyId=C8CA14D0-05EA-4A44-AE78-F5E4DF6208AF&displaylang=en
[4] Télécharger Entlib 4.1 :
http://www.microsoft.com/downloads/details.aspx?FamilyId=1643758B-2986-47F7-B529-3E41584B6CE5&displaylang=en

Encodage des urls

Un récent article de Rick Strahl me rappelle un problème rencontré sur un projet.

J'avais la charge de l'écriture d'un frontal qui consolidait des résultats reçus de divers services Web. Nous avons passé pas mal de temps à nous bagarrer contre des problèmes de codage/décodage incompatibles entre eux et qui apparaissaient de façon en apparence aléatoire…

Que se passait-il?

Pour diverses raisons ces services pouvaient (ou non) encoder certaines données de différentes façons. De mon côté je devais donc faire l'opération inverse pour afficher correctement le résultat et permettre aux utilisateurs d'éventuellement les modifier. Oui, mais laquelle, selon quels critères? Bref on s'est pas mal amusé…

L'espace de noms System.Web contient les méthodes d'encodages suivantes:

1. UrlEncoding
2. UrlDecoding
3. HtmlEncoding
4. HtmlDecoding

Decoding défaisant ce qu'Encoding fait.

On retrouve également UrlEncoding/Decoding dans System.

La question que l'on peut se poser est a quoi servent autant de méthodes. Voici un exemple adapté du papier de Rick Strahl qui devrait nous servir de base de discussion:

string test = 
"This is a value & I don't care for it.\t\"quoted\" 'single quoted',<% alligator %>#";

string UrlHttpUtility_Encoded = System.Web.HttpUtility.UrlEncode(test);
string UriDataEscaped_Encoded = System.Uri.EscapeDataString(test);
string UriEscaped_Encoded = System.Uri.EscapeUriString(test);

Le résultat est résumé ici ans l'ordre où ils sont générés:

This+is+a+value+%26+I+don't+care+for+it.%09%22quoted%22+'single+quoted'%2c%3c%25+alligator+%25%3e%23
This%20is%20a%20value%20%26%20I%20don't%20care%20for%20it.%09%22quoted%22%20'single%20quoted'%2C%3C%25%20alligator%20%25%3E%23
This%20is%20a%20value%20&%20I%20don't%20care%20for%20it.%09%22quoted%22%20'single%20quoted',%3C%25%20alligator%20%25%3E#

Ces méthodes disposent d'une méthode de décodage. Euuh, en fait pas toutes. Par exemple je n'ai rien trouvé pour EscapeUriString.

Il se pose donc la question de la compatibilité de ces méthodes entre elles, ce que nous pouvons tester derechef! (derechef: on m'aurait dit que je caserais un jour ce mot…)

string s1 = 
     System.Uri.UnescapeDataString(UrlHttpUtility_Encoded);
string s2 = 
     System.Web.HttpUtility.UrlDecode(UrlHttpUtility_Encoded);

string s3 = 
     System.Web.HttpUtility.UrlDecode(UriDataEscaped_Encoded);
string s4 = 
     System.Uri.UnescapeDataString(UriDataEscaped_Encoded);

string s5 = System.Web.HttpUtility.UrlDecode(UriEscaped_Encoded);
string s6 = System.Uri.UnescapeDataString(UriEscaped_Encoded);

On obtient alors:

Soit 5 résultats corrects sur 6, ce n'est pas si mal! Et surtout, remarquez que la variante Decode sait traiter correctement sa sœur en Encode.

La conclusion est qu'il est peut être préférable de ne pas encoder ses url avec System.Web.HttpUtility.UrlEncode, si on peut l'éviter, à moins d'être certains d'employer la bonne méthode de décodage. Ce qui suppose de connaître à l'avance la méthode d'encodage et c'était justement un de nos problèmes…

Et sur le fond, c'est une assez bonne nouvelle comme le souligne Rick Strahl. En effet, on n'a alors plus besoin de référencer System.Web qui pèse tout de même 2,5 Mo dans une application non Web pour faire une malheureuse opération d'encodage/décodage d'url.

Pour encoder on utilise EscapeDataString(), mais pour décoder:

public static string UrlDecode(string text)
{
    // au cas où l'encodage a été fait avec 
    // System.Web.HttpUtility.UrlEncode()
    text = text.Replace("+", " ");
    return System.Uri.UnescapeDataString(text);
}

Oui, il faut tout de même prévoir le cas où l'url a été encodée avec System.Web. On a donc une méthode universelle de décodage qui ne passe pas par System.Web. Rick Strahl montre aussi une fonction qui sait récupérer une donnée dans une chaîne de requête encodée.

Pour éliminer la référence à System.Web il reste encore à traiter le cas de l'encodage HTML. La solution proposée est la suivante:

public static string HtmlEncode(string text)
{
    if (text == null)
    {
        return null;
    }

    StringBuilder sb = new StringBuilder(text.Length);

    int len = text.Length;
    for (int i = 0; i < len; i++)
    {
        switch (text[i])
        {
            case '<':
                sb.Append("&lt;");
                break;
            case '>':
                sb.Append("&gt;");
                break;
            case '"':
                sb.Append("&quot;");
                break;
            case '&':
                sb.Append("&amp;");
                break;
            default:
                if (text[i] > 159)
                {
                    // decimal numeric entity
                    sb.Append("&#");
                    sb.Append(((int)text[i]).ToString(CultureInfo.InvariantCulture));
                    sb.Append(";");
                }
                else
                {
                    sb.Append(text[i]);
                }
                break;
        }
    }
    return sb.ToString();
}

La principale différence entre cette solution et l'implémentation proposée par Microsoft est qu'à Redmond ils encodent avec des entités uniquement sur la plage [160-255] et non pas tout ce qui est au dessus de 159. Si quelqu'un a une explication…

Bibliographie

Article de Rick Strahl:

http://www.west-wind.com/weblog/posts/617930.aspx

Message subliminal à Sami:
C'est quand que tu nous installe la version suivante de B2Evolution que tu nous a fais miroiter il y a un an?

Trucs en vrac pour démarrage en douceur

Outre mes meilleurs vœux à vous tous, je voudrai signaler un extraordinaire tutoriel sur Entity Framework:

http://weblogs.asp.net/zeeshanhirani/archive/2008/12/08/my-christmas-present-to-the-entity-framework-community.aspx

500 pages tout de même!!
Ce n'est pas tout, Zeeshan Hirani a gentiment accepté que je traduise en français son travail. Ce ne sera pas prêt pour la semaine prochaine, mais c'est en cours!

Sur un autre sujet Imar Spaanjaars a publié sa version pour .NET 3.5 de son tutoriel sur le modèle NTiers:

http://imar.spaanjaars.com/QuickDocId.aspx?quickdoc=476

Bon pour l'instant il n'y a encore que les deux premières parties d'une série de 6. Mais en attendant vous pouvez lire la version pour .NET 2.0 (même lien).

Encore un truc que j'aimerai bien avoir le temps de traduire…

Autre annonce, Microsoft vient de publier une galerie de modèles de site adaptés pour son framework MVC. Vous pouvez les réutiliser ou ajouter vos œuvres, c'est ici:

http://www.asp.net/mvc/gallery/default.aspx?supportsjs=true

Et je termine en signalant un petit blog amusant de François Remy sur tous ces fichiers système qui nous pourrissent les répertoires:

http://blogs.developpeur.org/fremycompany/archive/2009/01/01/hit-parade-ces-fichiers-qui-polluent-vos-dossiers.aspx