samedi 5 septembre 2009

Import/Export des schémas d’une BD SQL Server

Après plusieurs recherche chez Microsoft pour réaliser cette manipulation, je n’ai rien trouvé de vraiment concret, juste les liens suivant pour SQL 7 & 2000 et SQL 2005.

Ce que j’apprend c’est  qu’anciennement les diagrammes étaient sauvés dans la table dtproperties et maintenant sous SQL 2005 dans la table sysdiagrams.

En fait la meilleure solution que j’ai trouvé ce trouve sur le site de The Code Project. L’article est en anglais, mais je vais faire un tuto rapide, il n’y a rien de compliquer.

  1. Downloader le fichier Zip qui comporte 2 fichiers, à savoir :
    - Tool_ScriptDiagram2005.sql
    - Tool_VarbinaryToVarcharHex.sql

    Tool_ScriptDiagram2005.sql contient la procédure stocké qui vas extraire les informations de la table sysdiagrams et lui appliquer un template, afin de rendre autonome le code sql généré pour importer le schéma dans une autre bd ultérieurement.

    Tool_VarbinaryToVarcharHex.sql est une fonction qui est appelé par la procédure stocké.
    Elle transforme le binaire du schéma en une liste de valeur hexadecimal exploitatble dans un ficher sql.

  2. Ouvrir SQL Server et intégrer les 2 scripts sql dans la BD qui vous intéresse.

  3. Pour extraire un schéma. Demander une nouvelle requête afin de saisir et exécuter la commande suivante :
    exec Tool_ScriptDiagram2005 'LeNomDeMonSchema'
    Sauvegarder le résultat généré dans un fichier, par exemple monSchéma.sql.

  4. Pour importer un diagramme, rien de plus simple ouvrir le fichier sql du schéma généré et l'exécuter.
    La procédure stocké et la fonction "Tool_..." ne sont pas nécessaire. Le schéma sera recréé avec le même nom suivi de la date et l'heure d'import pour ne pas écraser le schéma existant.

Dans mon cas, je restaure souvent ma BD et je repars à vide donc je préfère que la date et l'heure ne soit pas présent derrière le nom du diagramme.
Pour cela j'ai modifié la procédure stocké existante "Tool_ScriptDiagram2005" en ajoutant les lignes de code suivantes:

  PRINT '    PRINT ''Removed uniqueness when diagram ' + @name + ' not exist'''
  SELECT @line =  
        '    IF not exists (SELECT [name] FROM sysdiagrams WHERE [name] = '''+ @name +''') '
  PRINT @line
  PRINT '        SET @DiagramSuffix = '''''

à insérer juste avant la ligne existante :

  PRINT '    PRINT ''Write diagram ' + @name + ' into new row (and get [diagram_id])'''

Tout ça, c'est bien, mais généralement on a plein de diagramme à gérer, donc le faire manuellement ce n'est pas le top. Il est possible d'utiliser des fichiers batch pour aller plus vite.

Pour extraire des schémas (Extract.bat):

@set SERVER=(local)
@set DB=NomDeMaDatabase

sqlcmd -d %DB% -E -S %SERVER% ^
 -Q "exec Tool_ScriptDiagram2005 'NomDeMonSchema'" ^
 -o NomDeMonSchema.sql

Pour Importer des schémas (Import.bat):

@set SERVER=(local)
@set DB=NomDeMaDatabase

:VERIF_ARGS
@REM Si arguments non vide on les prends en compte
@IF NOT "%~1"==""  @set SERVER=%~1
@IF NOT "%~2"==""  @set DB=%~2

:IMPORT
sqlcmd -d %DB% -E -S %SERVER% ^
 -i NomDeMonSchema1.sql ^
 -i NomDeMonSchema2.sql ^
 -i NomDeMonSchema3.sql ^
 -o OUTPUT_DIAGRAMS_IMPORT.log 

La section :VERIF_ARGS est utile si on a un autre batch qui l'appel. L'idée est d'être capable de l'appliquer pour différents environnement, comme Test ou Préproduction par exemple, par défaut tout est local.

L'option "-o OUTPUT_DIAGRAMS_IMPORT.log" permet d'avoir une log de l'exécution des imports afin de traquer les erreurs.

Voici un exemple du batch de restauration de ma bd en local qui appel l’import et met en place les 2 SQL pour gérer les diagrammes :

@echo on

@set SERVER=(local) 
@set DB=NomDeMaDatabase

:Restore DB - Version nnn
sqlcmd -d %DB% -E -S %SERVER%  -i Restore.sql -o OUTPUT_RESTORE.txt:CREATE DIAGRAMS
@echo.
@echo ** CREATE DIAGRAMS **
@cd Diagrams
@call import.bat %SERVER% %DB%
@echo.
@echo ** SQL TOOLS FOR DIAGRAMS **
sqlcmd -d %DB% -E -S %SERVER% -i "Tool_ScriptDiagram2005.sql"
sqlcmd -d %DB% -E -S %SERVER% -i "Tool_VarbinaryToVarcharHex.sql"
@cd ..

Tous mes scripts de schémas et tools sont mis dans un répertoire que j’ai nommé Diagrams.

Et un exemple du fichier Restore.sql pour avoir une solution complête :

DECLARE @TARGET_MDF VARCHAR(MAX)
DECLARE @TARGET_LDF VARCHAR(MAX)
DECLARE @SOURCE VARCHAR(MAX)

SET @SOURCE = 'C:\...\NomDuBackupDeMaBD.BAK' 
SET @TARGET_MDF = 'C:\...\MSSQL\Data\NomDeMaBD.mdf'
SET @TARGET_LDF = 'C:\...\MSSQL\Data\NomDeMaBD_Log.ldf'

USE master

PRINT '-- Drop DB --'

IF  EXISTS (SELECT name FROM sys.databases WHERE name = N'NomDeMaBD')
    ALTER DATABASE [NomDeMaBD] SET SINGLE_USER WITH ROLLBACK IMMEDIATE

IF  EXISTS (SELECT name FROM sys.databases WHERE name = N'NomDeMaBD')
    DROP DATABASE [NomDeMaBD]


PRINT '-- Restore DB --'

RESTORE DATABASE [NomDeMaBD]
FROM DISK = @SOURCE
WITH 
MOVE 'NomDeMaBD' TO @TARGET_MDF,
MOVE 'NomDeMaBD_log' TO @TARGET_LDF

Voilà, bon usage.

Pour avoir une BD de démo, au besoin, je vous propose de prendre AdvenureWorks ici.

mercredi 26 août 2009

Comment garder l’ancienne version de Live ou MSN Messenger ?

J’adore ce que fait Microsoft en général mais là je trouve qu’il y a de l’abus et ce n’est pas la première fois concernant MSN Messenger. Je craque.

La nouvelle version de MSN Messenger, nommé Live Messenger nous force à installer plein de chose sur sont poste sans qu’on les ai demandées. Ex : Je lance l’installation de Live Messenger, le setup est tout petit mais ce n’est qu’un prélude à tous les outils Live Microsoft … On décoche tous les produits en ne gardant que Live Messenger et là 70 meg à installer, il pèse lourd le bébé, on est loin des 15meg d’il y a 1 ou 2 ans. Ils travaillent fort les gars de Microsoft.

Ok, Ok, d’ac, je lance l’installation, et je regarde la barre d’avancement, et là, je vois quoi RepportError qui s’installe, j’ai jamais demandé ça moi. GGRRrrrrrr……

Ou dans le style, vous avez l’ancienne version de Live Messenger ou MSN Messenger qui répond complètement à vos besoins, vous lancez l’appli et là un super message vous dit qu’il existe une nouvelle version, vous répondez non, pas de message et pas de connexion, on recommence, et bah! zut! même chose plus possible de se connecter, cela ne marche plus, on refais qqes essai, même chose. Ils font quoi chez Bilou,  GGRRrrrrrr……

Là, sérieux, j’aime vraiment pas ces manière de faire. Par contre c'est une bonne manière pour perdre de la clientèle. Je serais eux, je ne pousserais pas trop loin le bouchon.

Ok, on se calme. Donc comment faire pour garder son ancienne version de Live ou MSN Messenger, voici la procédure :

  • Fermer votre Live ou MSN Messenger.
  • Aller dans le répertoire C:\Program Files\MSN Messenger.
  • Retrouver le fichier msnmsgr.exe.
  • Demander les Propriétés et allez dans l’onglet Compatibilité.
  • Changer le mode de compatibilité pour Windows 2000 et cliquer OK.
  • Redémarrer Live ou MSN Messenger, le programme de réparation de l’application peut démarrer, laisser le faire.
  • Redémarrer Live ou MSN Messenger une dernière fois, ca y est tout est ok maintenant.

Ça fait du bien de s’exprimer des fois :)

MAJ : 2009/09/16 Il semblerait que Microsoft force les gens à passer sous la dernière version de MSN Messenger, en bloquant définitivement avec le même message, l’utilisation des anciennes version. Encore là, l’astuce est de prendre le mode de compatibilité Windows 2000 et de fermer puis relancer votre MSN Messenger. C’est le seul qui reste opérationnel, malheureusement.

vendredi 14 août 2009

L’instruction yield pour récupérer la sélection d’éléments d’une liste

L'idée n'est pas de faire un cours sur l'instruction yield, pour cela voir les liens suivants :

http://msdn.microsoft.com/fr-fr/library/9k7k7cf0.aspx

http://romainverdier.developpez.com/articles/dotnet/le-mot-cle-yield-et-les-iterateurs-en-csharp/

Mais de donner un petit plus quand on code avec des listes et très souvent on refait la même chose à savoir récupérer la sélection d'une liste pour lui appliquer un traitement particulier.

Donc le but du jeu est ; Comment peut-on faire pour éviter la duplication des Foreach ou While ou autre afin de récupérer une sélection d’éléments ?
Pour être le plus souple possible, je propose d’utiliser une méthode, nommé Cast, qui soit le plus générique possible.

        // Méthode qui permet d'extraire les objets d'une collection.
        // Valable à partir de Framework .NET 2.0
        // On peut mettre cette méthode dans une classe d’aide(Helper)
        // de facon à l'appeler ailleur pour tout objet qui contient
        // une collection IEnumerable (listBox,)
        IEnumerable<T> Cast<T>(IEnumerable items)
        {
            foreach (object item in items)
                yield return (T)item;
        }

Dans l’exemple suivant, j’utilise une class nommé Personne comme type avec deux propriétés, à savoir : Nom et Prenom. L’utilisation d’un objet string ou autre est possible aussi (voir l’exemple dans le code complet).
Voila le code dans sa totalité.

Merci Éric pour tes idées :

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Collections;
using System.Windows.Forms;

namespace Yield_SelectedItems
{
    public partial class Form1 : Form
    {

        public Form1()
        {
            InitializeComponent();

            List<Personne> personnes = new List<Personne>();
            personnes.Add(new Personne("Hugo", "Victor"));
            personnes.Add(new Personne("Zola", "Emile"));
            personnes.Add(new Personne("Dupont", "Thomas"));
            personnes.Add(new Personne("Dupond", "Daniel"));
            personnes.Add(new Personne("Super Dupond", "Daniel"));

            listBox1.Items.Clear();
            listBox1.Items.AddRange(personnes.ToArray());

        }

        private void button1_Click(object sender, EventArgs e)
        {
            // Récupérer que les objets sélectionnés
            List<Personne> maSelections = new List<Personne>(Cast<Personne>(listBox1.SelectedItems));

            // On créer un objet list de type (chaine de caractère) pour générer
            // avec la méthode Join un saut de ligne facilement, sans avoir à 
            // gérer le premier ou le dernier retour chariot de trop.
            List<string> s = new List<string>();
            foreach (Personne onePersonne in maSelections)
            {
                s.Add(string.Format("{0} {1}", onePersonne.Prenom, onePersonne.Nom));
            }
            String nomPrenom = string.Join("\r", s.ToArray());

            // On affiche le résultat.
            System.Windows.Forms.MessageBox.Show(nomPrenom);


            // Démo avec une list de string
            ListBox mesProduits = new ListBox();
            mesProduits.SelectionMode = SelectionMode.MultiExtended;
            mesProduits.Items.Clear();

            // J'alimente ma listbox
            List<string> listProduits = new List<string>();
            listProduits.Add("Produit A");
            listProduits.Add("Produit B");
            listProduits.Add("Produit C");
            listProduits.Add("Produit D");
            listProduits.Add("Produit E");
            mesProduits.Items.Clear();
            mesProduits.Items.AddRange(listProduits.ToArray());

            // Je sinule une sélection
            mesProduits.SelectedItems.Add(listProduits[0]);
            mesProduits.SelectedItems.Add(listProduits[3]);

            // On récupère les produits sélectionnés
            List<string> monChoix = new List<string>(Cast<string>(mesProduits.SelectedItems));

            // On les affiches.
            System.Windows.Forms.MessageBox.Show(string.Join("\r", monChoix.ToArray()));


            // Lol, plus simple. Comme quoi des fois...
            Personne[] essai2 = new Personne[listBox1.SelectedItems.Count * 2]; // Je double la taille pour tester le mode Append
            listBox1.SelectedItems.CopyTo(essai2, 0);
            listBox1.SelectedItems.CopyTo(essai2, 3); // Append mode
            List<string> s2 = new List<string>();
            foreach (Personne onePersonne in essai2)
            {
                s2.Add(string.Format("{0} {1}", onePersonne.Prenom, onePersonne.Nom));
            }
            // On les affiches.
            System.Windows.Forms.MessageBox.Show(string.Join("\r", s2.ToArray()));


            // Lol, plus simple. Comme quoi des fois...
            string[] essai3 = new string[mesProduits.SelectedItems.Count];
            mesProduits.SelectedItems.CopyTo(essai3, 0);
            List<string> soluce3 = new List<string>(essai3);
            // On les affiches.
            System.Windows.Forms.MessageBox.Show(string.Join("\r", soluce3.ToArray()));

        }


        // Méthode qui permet d'extraire les objets d'une collection.
        // Valable à partir de Framework .NET 2.0
        // On peut mettre cette méthode dans une classe d’aide(Helper)
        // de facon à l'appeler ailleur pour tout objet qui contient
        // une collection IEnumerable (listBox,)
        IEnumerable<T> Cast<T>(IEnumerable items)
        {
            foreach (object item in items)
                yield return (T)item;
        }

    }


    class Personne
    {
        private string _nom;
        private string _prenom;

        public Personne()
        {
        }

        public Personne(string nom, string prenom)
        {
            Nom = nom;
            Prenom = prenom;
        }

        public string Nom
        {
            get { return _nom; }
            set { _nom = value; }
        }

        public string Prenom
        {
            get { return _prenom; }
            set { _prenom = value; }
        }

        // l'override de la méthode string est utilisé pour populer le listbox.
        public override string ToString()
        {
            return string.Format("{0} {1}", Nom, Prenom);
        }
    }

}

mardi 28 juillet 2009

Comment copier les colonnes d’entête de la grille de résultat d’une requête SQL ?

Voici une petite astuce bien pratique pour récupérer, lors d’un couper/coller, les colonnes de l’entête quand on est en mode grille dans Microsoft SQL Server Management 2005 :

Allez dans Outils/Options../Résultat des requêtes/SQL Serveur/Mode texte

et cocher l’option  Inclure l’entête de colonne dans la copie de la grille résultat.

Attention : Il faut malheureusement fermer complètement SQL Server Manager et le ré-ouvrir. N’oublier pas de sauvegarder votre requête avant.

jeudi 23 juillet 2009

RTM Windows 7 is available

C’est fait, Microsoft c’est décidé, la version officiel de Windows 7 est disponible.

Pour plus d’information, voir le post (en anglais) de cette annonce sur le blog de Microsoft ici.

Il semblerait que pour les gens qui possèdent un compte MSDN, la version téléchargeable ne soit pas disponible avant le 6 aout en version anglaise:(.

Cette version date du 13 juillet compilé à 12h55 et a comme numéro de build : 7600.16385.090713-1255

Vivement la version Française.

vendredi 17 juillet 2009

Publier du code avec SyntaxHighlighter sur Blogger


L’installation se fait en 4 étapes :

  1. On télécharge la version que l’on veut ici (v1.5.1) ou ici (v2.0.nnn)
  2. On extrait le contenu pour héberger les fichiers sur un site ou en utilisant l’un des liens suivant :
  3. On se connecte sur son bloque et l’on vas dans (Tableau de bord / Mise en page / Modifier le code HTML) pour modifier le modèle du blogue en ajoutant quelques lignes en fonction de la version. On ne se casse pas la tête, il faut aller à la fin du modèle et juste après la ligne suivante “<!-- end outer-wrapper -- >” on insère le code suivant :
  • Version 1.5.1
<link href='http://alexgorbatchev.com/pub/sh/1.5.1/styles/SyntaxHighlighter.css' rel='stylesheet' type='text/css'/>

<script language='javascript' src='http://alexgorbatchev.com/pub/sh/1.5.1/scripts/shCore.js'/>
<script language='javascript' src='http://alexgorbatchev.com/pub/sh/1.5.1/scripts/shBrushJScript.js'/>
<script language='javascript' src='http://alexgorbatchev.com/pub/sh/1.5.1/scripts/shBrushCSharp.js'/>

<script language='javascript'>
dp.SyntaxHighlighter.BloggerMode();
dp.SyntaxHighlighter.HighlightAll('code');
</script>
  • Version 2.0.nnn
<link href='http://alexgorbatchev.com/pub/sh/2.0.320/styles/shCore.css' rel='stylesheet' type='text/css'/> 
<link href='http://alexgorbatchev.com/pub/sh/2.0.320/styles/shThemeDefault.css' rel='stylesheet' type='text/css'/>

<script src='http://alexgorbatchev.com/pub/sh/2.0.320/scripts/shCore.js' type='text/javascript'></script>
<script src='http://alexgorbatchev.com/pub/sh/2.0.320/scripts/shBrushCSharp.js' type='text/javascript'></script>
<script src='http://alexgorbatchev.com/pub/sh/2.0.320/scripts/shBrushJScript.js' type='text/javascript'></script>

<script type='text/javascript'>
SyntaxHighlighter.config.bloggerMode = true
SyntaxHighlighter.all();
</script>

Après plusieurs heures de recherche et d’essai (merci au post de Burca Victor Ciprian). Le truc, pour faire fonctionner SyntaxHighlighter correctement avec Blogger, c’est la ligne suivante qui doit être ajouter dans modèle du blogue (Ligne déjà présente dans les scripts précédents) :

  • Pour la version 1.5.1 :  dp.SyntaxHighlighter.BloggerMode();
  • Pour la version 2.0.nnn :   SyntaxHighlighter.config.bloggerMode = true

Les gens qui ne sont pas avec Blogger, doivent retirer cette ligne.

Si on ne met pas ces lignes, on obtient le cas suivant avec un code incompréhensible et plein de balise <BR />. Exemple :

  4.  Dernière phase, il ne reste plus qu’à formater le code que l’on veux publier avec les balises suivantes :

  • Pour la v1.5.1 :
    <pre name="code" class="[non du langage]">
    [ici votre code]
    </pre>
  • Pour la v2.0.nnn :
    <pre class="brush: [nom du langage];"
    [ici votre code]
    </pre>
Il faut remplacer [nom du langage] par le nom du langage (exemple : CSharp, js, etc…) et [ici votre code] par le morceau de code que vous voulez présenter. N’oublier pas d’ajouter le lien du langage que vous voulez afficher dans le modèle de votre blogue.

Voila, bon blogue à vous :).

Comment trier un objet hashtable sur les valeurs ?

D'accord on est mal parti avec ce titre, car par définition, un objet hashtable n'est pas triable sur les valeurs.

Ce type d'objet est toujours triè sur les clés que l'on insère. À chaque foi que l'on ajoute une entrée, la clé est haché http://fr.wikipedia.org/wiki/Table_de_hachage pour définir sa position dans la table et non pas ajouter l'une derrière l'autre comme dans un tableau ordinaire.

De plus, si on écrit du nouveau code, certainement qu'un objet hashtable n'est pas la bonne manière de faire. Surtout avec les génériques : List<>.

Alors, comment on en arrive là ? En générale, ce cas arrive sur du code existant pour lequel on ne veut pas tout réécrire, à l'origine le besoin était plus simple, question de temps, etc...

Pour les personnes pressées, voici le code de la solution :

using System;
using System.Collections.Generic;
using System.Text;

using System.Collections;

namespace SortHashtable
{
    class Program
    {
        static void Main(string[] args)
        {

            //...
            Hashtable hashtable = new Hashtable();
            hashtable.Add("keyV", 55);
            hashtable.Add("keyG", 99);
            hashtable.Add("keyB", 25);
            hashtable.Add("keyD", 13);
            hashtable.Add("keyL", 77);

            //On affiche les éléments avant le tri.
            System.Console.WriteLine("Avant le tri dans l'objet Hashtable:");
            foreach (DictionaryEntry dicEntry in hashtable)
            {
                System.Console.WriteLine("Key={0} Value={1}", dicEntry.Key, dicEntry.Value);
            }


            IComparer dicEntryComparer = new ComparerDictionnaryEntryOnDoubleValue();

            //On déclare un arraylist.
            ArrayList al = new ArrayList();

            //On ajoute la hashtable dans un arraylist.
            al.AddRange(hashtable);

            //On tri la liste.
            al.Sort(dicEntryComparer);

            //On affiche les éléments de l'arraylist trié.
            System.Console.WriteLine("\nAprès le sort sur les valeurs dans l'objet ArrayList (ordre croissant):");
            foreach (DictionaryEntry dicEntry in al)
            {
                System.Console.WriteLine("Key={0} Value={1}", dicEntry.Key, dicEntry.Value);
            }

            //On fait une pose pour voir le résultat.
            System.Console.ReadKey();

        }
    }

    public class ComparerDictionnaryEntryOnDoubleValue : IComparer
    {
        int IComparer.Compare(object right, object left)
        {
            // Conversion en double des valeurs.
            double l = Convert.ToDouble(((DictionaryEntry)left).Value);
            double r = Convert.ToDouble(((DictionaryEntry)right).Value);

            // On compare l'élément de gauche avec celui de droite.
            // Le boolean qui en résulte informe si l'élément de gauche
            // est plus grand que celui de droite.
            //return (Comparer<double>.Default.Compare(l, r)); // Tri en ordre décroissant.
            return (Comparer<double>.Default.Compare(r, l)); // Tri en ordre croissant.
        }
    }

}
Voici le résutat que vous devez avoir :

mardi 19 mai 2009

Présentation

Bonjour, L'idée de ce blog est née d'un constat récurent : "Mais j'ai déjà rencontré ce problème...". C'est pourquoi je me propose de mettre à disposition du code simple et réutilisable à tous les problèmes que régulièrement l'on retrouve dans notre développement. Merci à Éric P. sans qui je n'aurais pas ouvert ce blog.

Membres