Bienvenue à Blogs CodeS-SourceS Identification | Inscription | Aide
Depuis quelques temps, je travaille en utilisant de nouveaux outils et l'un d'entre eux est TFS (... Je sais, je sais, ça fait longtemps que ça existe ! Mais, j'utilisais SVN Stick out tongue).
Devant assurer le suivi et le support d'un projet d'un consultant qui a fini sa mission, je me suis mis à la recherche de toutes les documentations de ce projet en interne... Malheureusement rien Sad. Une des particularités de ce projet vient du fait aussi que, ce qu'il a développé , est un patch/correctif pour une application existante... Donc, il a modifié des fichiers de l'application principale, et malheureusement aussi, sans avoir laissé de changelog ou de traces écrites. La seule façon pour moi de voir quels sont les fichiers impactés et de savoir était donc de regarder l'historique dans TFS.

Etant donné la grande quantité d'historique et de modifications faites, et afin de mieux cibler quels fichiers j'allais pouvoir modifier, j'ai développé un tout petit script permettant de récupérer l'historique d'un projet vers un fichier csv (que je vais trier manuellement). Pourquoi ai je procédé ainsi ? Parce que, je n'ai pas trouvé la possibilité de générer via TFS un simple report d'historique ...

Voici la liste des namespaces nécessaires :

using System;
using System.Collections;
using System.IO;
using System.Net;

using Microsoft.TeamFoundation.Client;
using Microsoft.TeamFoundation.VersionControl.Client;


Pour les 2 derniers, il est indispensable que vous ayez installé le SDK de TFS. (Trouvable via le SDK de Visual Studio).

Ensuite, voici le bout de code permettant d'arriver à votre fin (récupérer l'historique d'un projet) :

TeamFoundationServer tfsServer = new TeamFoundationServer("TFSServerAddress", CredentialCache.DefaultCredentials);
VersionControlServer vcs = (VersionControlServer)tfsServer.GetService(typeof(VersionControlServer));

String bp = "$/Projects/MySubProjects";
String output = @"D:\tfs_report.csv";

IEnumerable cs = vcs.QueryHistory(bp, VersionSpec.Latest, 0, RecursionType.Full, null /* ou alors, le nom d'un utilisateur */, null, null, Int32.MaxValue, true, true);

try {
    using (StreamWriter sw = new StreamWriter(output, false)) {
sw.WriteLine("ChangeSet;User;Date;Comments;ItemId;FileName;Folder;Changes;ItemType");
   foreach (Changeset c in cs)
   {
       if (c.Changes.Length > 0)
       {
         foreach (Change ch in c.Changes) {
           sw.Write("{0};{1};{2};{3};", c.ChangesetId, c.Committer, c.CreationDate, c.Comment);
           sw.Write("{0};{1};{2};{3};{4}", ch.Item.ItemId, Path.GetFileName(ch.Item.ServerItem), Path.GetDirectoryName(ch.Item.ServerItem), ch.ChangeType, ch.Item.ItemType);
           sw.WriteLine();
         }
       }
   }
  }
}
catch (Exception ex) {
    throw ex;
}


Il existe surement d'autres outils permettant de le faire plus rapidement ou d'autres méthodes, mais, c'est la plus rapide qui m'est venue à l'esprit! Smile

Voilà ! Bonne fin de journée.

A +
2 commentaire(s)
Classé sous : ,
Je dois avouer que ça fait longtemps que je n'avais pas fait touché à ASP.NET. Un peu comme Richard, j'avais commencé à m'intéresser à ASP.NET MVC il y a à peine 2 mois (lui aussi, très récemment!). Je dois toujours avouer qu'après avoir joué avec pendant quelques temps, je suis un peu déstabilisé quand j'essaie de revenir vers ASP.NET 3.5...
Dans le bon sens du terme Big Smile ! Le développement Web est, il faut l'avouer, plus facile avec et tout aussi puissant (pour ce que j'ai pu en faire). Pour un projet perso, je dois utiliser de l'ASP.NET ... J'ai eu tellement de mal que je me suis créé des classes pour faciliter mon développement.

Un petit exemple :

/// <summary>
///
/// </summary>
public static class HtmlHelper
{
/// <summary>
/// Permet de créer un champs de type input text Html à la façon de ASP.NET MVC
/// </summary>
/// <param name="fieldName">Nom du champs</param>
/// <returns></returns>
public static String TextBox(String fieldName)
{
if (String.IsNullOrEmpty(fieldName))
throw new ArgumentException("You must specified a value to create a textbox field");

return String.Format(@"<input type=""text"" name=""{0}"" value=""""/>", fieldName);
}

/// <summary>
/// Permet de créer un champs de type input password Html à la façon de ASP.NET MVC
/// </summary>
/// <param name="fieldName">Nom du champs</param>
/// <returns></returns>
public static String Password(String fieldName)
{
if (String.IsNullOrEmpty(fieldName))
throw new ArgumentException("You must specified a value to create a password field");

return String.Format(@"<input type=""password"" name=""{0}""/>", fieldName);
}

/// <summary>
/// Permet d'encoder une chaine (pour éviter les injections :) à la façon de ASP.NET MVC
/// </summary>
/// <param name="text"></param>
/// <returns></returns>
public static String Encode(String text)
{
if (text == null)
throw new ArgumentException("You can not encode a null field");

return HttpContext.Current.Server.HtmlEncode(text);
}

/// <summary>
///
/// </summary>
/// <param name="LinkText"></param>
/// <param name="LinkUrl"></param>
/// <returns></returns>
public static String ActionLink(String LinkText, String LinkUrl)
{
if (String.IsNullOrEmpty(LinkText) || String.IsNullOrEmpty(LinkUrl))
throw new ArgumentException("You can not create an action link with a blank/null argument");

return String.Format(@"<a href=""{0}"">{1}</a>", LinkUrl, LinkText);
}
}


Pour mes besoins, je n'ai pas besoin de l'ensemble des méthodes disponibles dans le classe HtmlHelper de MVC. Pratique en tout cas! Cela dit, je ne suis pas un Cyril, et je ne connais pas suffisamment la techno pour pouvoir les comparer ! Mais, c'est vrai que ça aide pas mal (^_^) !

Bien, allez, au boulot !

A +
8 commentaire(s)
Classé sous :
Toujours dans le même thème que mes précédents posts ( et ), je continuais mes utilisations des templates avec Silverlight. L'idée est de pouvoir créer un template modèle pour plusieurs contrôles de même type (En résumé, créer un thème).
Dans mon projet, je dispose de 5 boutons (voici un extrait de 2 boutons).

<Button Grid.Column="0"
x:Name="btnFirst"
Style="{StaticResource BtnStyle}"
Width="30"
HorizontalAlignment="Left"
Margin="0 2 0 2">
<Image Source="../Images/Controls/btnFirst.png" Height="15" Width="15" />
</Button>

<Button Grid.Column="1"
x:Name="btnFastRewind"
Style="{StaticResource BtnStyle}"
Width="30"
HorizontalAlignment="Left"
Margin="0 2 0 2">
<Image Source="../Images/Controls/btnFastRewind.png" Height="15" Width="15" />
</Button>



Voici le style que j'utilise (un peu similaire que ceux de mes précédents billets) :

<UserControl.Resources>
<vsm:Style x:Key="BtnStyle" TargetType="Button">
<vsm:Setter Property="Cursor" Value="Arrow"/>
<vsm:Setter Property="Background" Value="Transparent"/>
<vsm:Setter Property="Template">
<vsm:Setter.Value>
<ControlTemplate TargetType="Button">
<Grid>
<Grid.Resources>
<SolidColorBrush x:Key="BorderBrush" Color="#FFFFFF"/>
</Grid.Resources>
<vsm:VisualStateManager.VisualStateGroups>
<vsm:VisualStateGroup x:Name="CommonStates">
<vsm:VisualStateGroup.Transitions>
<vsm:VisualTransition Duration="0:0:0.3" To="MouseOver"/>
</vsm:VisualStateGroup.Transitions>
<vsm:VisualState x:Name="Normal"/>
<vsm:VisualState x:Name="MouseOver">
<Storyboard x:Name="MouseOverAnimation" >
<ColorAnimation
BeginTime="00:00:00"
Duration="0"
RepeatBehavior="Forever" AutoReverse="True"
Storyboard.TargetName="Background"
Storyboard.TargetProperty="(Rectangle.Fill).(SolidColorBrush.Color)"
To="#444444" />
</Storyboard>
</vsm:VisualState>
<vsm:VisualState x:Name="Pressed"/>
<vsm:VisualState x:Name="Disabled"/>
</vsm:VisualStateGroup>
</vsm:VisualStateManager.VisualStateGroups>

<Rectangle x:Name="Background"
Fill="{TemplateBinding Background}"
RadiusX="4" RadiusY="4"/>
<Rectangle x:Name="BackgroundGradient"
Stroke="{StaticResource BorderBrush}"
StrokeThickness="1" RadiusX="4" RadiusY="4"/>

<ContentPresenter
Content="{TemplateBinding Content}"
ContentTemplate="{TemplateBinding ContentTemplate}"/>
</Grid>
</ControlTemplate>
</vsm:Setter.Value>
</vsm:Setter>
</vsm:Style>
</UserControl.Resources>


Tout compile correctement, mais, le menu n'a pas le comportement que j'attends. En effet, avec ce code, quand on survole mes 2 premiers boutons, le changement d'état n'opère pas, puis (sous Firefox en tout cas), la couleur de Background reste active après avoir effectué un premier clic !

Après avoir posé la question sur les forums Silverlight, j'ai été éclairé sur les raisons de ce mauvais fonctionnement... Je cite :

[...] you need to set Background in your Button xaml because you rely on the TemplateBinding for the Rectangle.Fill property which can be any kind of Brush. So you'd better initialize it with Solid color Brush. Otherwise, the animation does not find the target. [...]

La solution se trouve donc dans le forum. ça me laisse cependant un peu perplexe, car j'étais pourtant sûr qu'en initialisant la propriété Fill du Rectangle (bindé à la propriété Background au début de mon style), au moins un type de Brush était initialisé... Je reviendrai cependant là dessus... (Sauf si vous avez une réponse supplémentaire :-) ).

Voilà!

A +



0 commentaire(s)
Classé sous :
Dans mon billet précédent,je rappelais un peu comment avec Silverlight on pouvait créer un bouton (Texte et Image) avec un effet de MouseOver. Ici, je vais faire juste rappeler comment créer un Contrôle qui sera facilement réutilisable (là aussi, rien de compliqué :-) ).

Pour ce faire, je créé un nouveau UserControl (en incluant son CodeBehind) TopMenuButton.xaml par exemple.

<UserControl
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
  xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
  mc:Ignorable="d"
  x:Class="MonProjet.TopMenuButton"
xmlns:vsm="clr-namespace:System.Windows;assembly=System.Windows">
  
  <UserControl.Resources>
    <vsm:Style x:Key="BtnTopMenuStyle" TargetType="Button">
     <vsm:Setter Property="Foreground" Value="#FFFFFF"/>
     <vsm:Setter Property="Cursor" Value="Arrow"/>
     <vsm:Setter Property="TextAlignment" Value="Left"/>
     <vsm:Setter Property="TextWrapping" Value="NoWrap"/>
     <vsm:Setter Property="FontSize" Value="10"/>
     <vsm:Setter Property="Template">
        <vsm:Setter.Value>
          <ControlTemplate TargetType="Button">
            <Grid>
              <Grid.Resources>
              <!-- Grid.Resources Here -->
              </Grid.Resources>
              <vsm:VisualStateManager.VisualStateGroups>
                <vsm:VisualStateGroup x:Name="CommonStates">
                  <vsm:VisualStateGroup.Transitions>
                    <vsm:VisualTransition Duration="0:0:0.3" To="MouseOver"/>
                  </vsm:VisualStateGroup.Transitions>
                  <vsm:VisualState x:Name="Normal"/>
                  <vsm:VisualState x:Name="MouseOver">
                    <Storyboard>
                    <ColorAnimation Duration="0"
Storyboard.TargetName="Background"
Storyboard.TargetProperty="(Rectangle.Fill).(SolidColorBrush.Color)"
To="Black" />
                    </Storyboard>
                  </vsm:VisualState>
                                      
                </vsm:VisualStateGroup>
                  
              </vsm:VisualStateManager.VisualStateGroups>
              <Rectangle x:Name="Background" RadiusX="4" RadiusY="4" Fill="{TemplateBinding Background}"/>
              <ContentPresenter
                HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"
                Padding="{TemplateBinding Padding}"
                VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}"
                Margin="4,5,4,4"
                Content="{TemplateBinding Content}"
                ContentTemplate="{TemplateBinding ContentTemplate}"
                TextAlignment="{TemplateBinding TextAlignment}"
                TextDecorations="{TemplateBinding TextDecorations}"
                TextWrapping="{TemplateBinding TextWrapping}"/>
            </Grid>
          </ControlTemplate>
        </vsm:Setter.Value>
      </vsm:Setter>
    </vsm:Style>
</UserControl.Resources>

<Button Background="Transparent"
x:Name="btnTopMenu"
Style="{StaticResource BtnTopMenuStyle}"
Loaded="btnTopMenu_Loaded">
   <StackPanel Orientation="Horizontal">
     <Image x:Name="imgTopMenu" Margin="0, 0, 5, 0" />
     <TextBlock x:Name="tblTopMenu" VerticalAlignment="Center" Margin="0" />
   </StackPanel>
</Button>

</UserControl>



Le bouton est rendu ici (par rapport au billet précédent) beaucoup plus générique, j'ai aussi rajouté l'évènement Loaded (j'en parlerai plus bas), et j'identifie chacun des éléments enfants de mon bouton.

Je vais créer 2 propriétés maintenant pour mon bouton. Une relative à l'Url de mon image (ImageSource), et l'autre relative au texte que je veux afficher pour le bouton (MenuText).


public String ImageSource
{
   get { return (String)GetValue(ImageSourceProperty); }
   set { SetValue(ImageSourceProperty, value); }
}

public String MenuText
{
   get { return (String)GetValue(MenuTextProperty); }
   set { SetValue(MenuTextProperty, value); }
}

public static readonly DependencyProperty ImageSourceProperty = DependencyProperty.Register(
"ImageSource", typeof(String), typeof(TopMenuButton), new PropertyMetadata(OnImageSourceChanged));

public static readonly DependencyProperty MenuTextProperty = DependencyProperty.Register(
"MenuText", typeof(String), typeof(TopMenuButton), new PropertyMetadata(OnMenuTextChanged));


private static void OnImageSourceChanged(DependencyObject o, DependencyPropertyChangedEventArgs e) {
   if (e.NewValue != null)
   {
     // Your code here
   }
}

private static void OnMenuTextChanged(DependencyObject o, DependencyPropertyChangedEventArgs e) {
   if (e.NewValue != null)
   {
     // Your code here
   }
}


L'utilisation des propriétés simples / DependencyProperty, y compris la création de propriétés, Thomas en parlait dans un de ses billets, il y a déjà bien longtemps. Nous n'y reviendrons donc pas :-).

Bien, notre gestionnaire d'évènement Loaded. Quand nos propriétés seront initialisées sur notre contrôle réutilisable, il faudra les initialiser aussi sur les contrôles enfants (le TextBlock et l'Image). C'est à dire que quand je vais mettre à jour la propriété ImageSource de mon TopMenuButton, il faudra mettre à jour la propriété Source de mon Image et etc... (Vous l'aviez déjà compris :-) ).

C'est lors de l'évènement Loaded qu'il faut donc le faire (et pas dans le constructeur de notre contrôle, car ce dernier doit d'abord être initialisé avant de pouvoir accéder aux propriétés des contrôles enfants qui le compose).

private void btnTopMenu_Loaded(object sender, RoutedEventArgs e)
{
   this.imgTopMenu.Source = ResourceHelper.GetBitmap(ImageSource);
   this.tblTopMenu.Text = MenuText;
}


ResourceHelper est une petite classe simple que j'ai trouvé et qui permet de charger à partir d'un chemin (et éventuellement du nom d'une assembly), une ressource (BitmapImage, String, Xaml, ou FontSource, etc...).

Voilà, notre contrôle est prêt. Dans ma page principale (.xaml), je rajoute à l'élement UserControl

xmlns:MonProjet="clr-namespace:MonProjet"

afin de pouvoir référencer puis utiliser mon nouveau contrôle de cette façon :

<MonProjet:TopMenuButton
x:Name="btnAddContent"
        Grid.Column="0"
        MenuText="Menu 01"
        ImageSource="Images/top_menu_01.png" />
      
      <MonProjet:TopMenuButton
x:Name="btnCreatePublicPage"
            Grid.Column="1"
            MenuText="Menu 02"
            ImageSource="Images/top_menu_02.png" />



Fin du rappel :-).

Bonne journée, et A bientôt.

0 commentaire(s)
Classé sous : ,
Un petit rappel concernant les templates et les "états" des contrôles.  J’ai voulu donc créer un bouton très simple. Il s'agit d'un contrôle Button très personnalisé (Une image à gauche, du texte à droite, pas d’arrière plan, ni de bordures sur le bouton, et un rollover changeant l'arrière-plan de notre nouveau contrôle).
Pas encore très à l’aise avec Blend, j’utilise donc son éditeur XAML donc pour écrire mes templates. Voici le code de mon bouton:

<Button Background="Transparent"
        x:Name="btnAddContent"
        Grid.Column="0"
        Style="{StaticResource BtnAddContentStyle}">
     <StackPanel Orientation="Horizontal">
         <Image Source="Images/menu_icon_01.png"
                Margin="0, 0, 5, 0" />
         <TextBlock Text="Add Content"
                    VerticalAlignment
="Center"
                    Margin="0" />
    </StackPanel>
</Button>

Mon premier réflexe (pas toujours les meilleurs) est de créer mon template comme celui ci (fonctionnel sur la Beta 1 ) :

<Style TargetType="Button" x:Key="Btn3Style4444">
  <Setter Property="FontSize" Value="10" />
  <Setter Property="Foreground" Value="White" />
  <Setter Property="Cursor" Value="Hand" />
  <Setter Property="Template">
    <Setter.Value>
      <ControlTemplate TargetType="Button">
        <Grid x:Name="RootElement">
          <Rectangle x:Name="Background"
                     RadiusY="5"
                     RadiusX="5" Background>
            <Rectangle.Fill>
              <SolidColorBrush x:Name="BackgroundBrush"
                               Color
="Transparent" />
            </Rectangle.Fill>
          </Rectangle>
          <ContentPresenter
            Content="{TemplateBinding Content}"
            ContentTemplate="{TemplateBinding ContentTemplate}"
            FontSize="{TemplateBinding FontSize}"
            Foreground="{TemplateBinding Foreground}"
            HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"
            TextAlignment="{TemplateBinding TextAlignment}"
            TextDecorations="{TemplateBinding TextDecorations}"
            TextWrapping="{TemplateBinding TextWrapping}"
            VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}"
          />
          <Grid.Resources>
            <Storyboard x:Key="MouseOver State">
              <ColorAnimation
                Duration="0"
                Storyboard.TargetName="BackgroundBrush"
                Storyboard.TargetProperty="Color"
                To="Black"
              />
            </Storyboard>
          </Grid.Resources>
        </Grid>
      </ControlTemplate>
    </Setter.Value>
  </Setter>
</Style>


Ce code qui pourtant compile sans problème ne me permettra pas de changer l’état de mon bouton. En effet, en générant un template à partir d’un contrôle existant, je me suis rappelé du Visual State Manager (je ne mets pas de lien sinon, je devrais en mettre trop ;p). Une des nouvelles fonctionnalités de Silverlight 2 Beta 2 qui permet très facilement de skinner ses contrôles.

Mon code précédent deviendra ainsi:

<vsm:Style x:Key="BtnAddContentStyle"
           TargetType
="Button">
        <vsm:Setter Property="Foreground" Value="#FFFFFF"/>
        <vsm:Setter Property="Cursor" Value="Arrow"/>
        <vsm:Setter Property="FontSize" Value="10"/>
        <vsm:Setter Property="Template">
          <vsm:Setter.Value>
            <ControlTemplate TargetType="Button">
              <Grid>
                <Grid.Resources>
                <!-- Grid.Resources Here -->
                </Grid.Resources>
                <vsm:VisualStateManager.VisualStateGroups>
                  <vsm:VisualStateGroup x:Name="CommonStates">
                    <vsm:VisualStateGroup.Transitions>
                      <vsm:VisualTransition Duration="0:0:0.3" To="MouseOver"/>
                    </vsm:VisualStateGroup.Transitions>
                    <vsm:VisualState x:Name="Normal"/>
                    <vsm:VisualState x:Name="MouseOver">
                      <Storyboard>
                      <ColorAnimation
                            Duration="0"
                            Storyboard.TargetName="Background"
                            Storyboard.TargetProperty="(Rectangle.Fill).(SolidColorBrush.Color)"
                            To="Black" />
                      </Storyboard>
                    </vsm:VisualState>
                  </vsm:VisualStateGroup>
                </vsm:VisualStateManager.VisualStateGroups>
                <Rectangle x:Name="Background"
                           RadiusX
="4"
                           RadiusY="4"
                           Fill
="{TemplateBinding Background}"/>
                <ContentPresenter
                  HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"
                  Padding="{TemplateBinding Padding}"
                  VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}"
                  Margin="4,5,4,4"
                  Content="{TemplateBinding Content}"
                  ContentTemplate="{TemplateBinding ContentTemplate}"
                  TextAlignment="{TemplateBinding TextAlignment}"
                  TextDecorations="{TemplateBinding TextDecorations}"
                  TextWrapping="{TemplateBinding TextWrapping}"/>
              </Grid>
            </ControlTemplate>
          </vsm:Setter.Value>
        </vsm:Setter>
      </vsm:Style>

Voilà pour le petit rappel.

Bonne soirée et bon skinning!


0 commentaire(s)
Classé sous :
De retour de vacances (enfin, presque ...), heureux et motivé comme tout, je me mets à jour côté news et installe comme tout le monde (enfin, presque) VS2008 SP1. Après 2 bonnes heures de téléchargements, je me mets à l'installer et j'ai ça :



Je regarde rapidemment donc les programmes installés, et je vois ça :



VS2008 existe bien, mais dans version anglaise (Ah, l'attention ...). Je prends le soin tout de même de désinstaller toutes les versions Express que j'avais d'installé (tiens, j'avais la version fr et en de Web Developer Express...).

Bon, je prends encore 1h30 cette fois ci pour télécharger la version anglaise !

En l'installant, j'ai ça :




... Je n'avais même pas la possibilité de choisir les composants que je désirai éventuellement pouvoir mettre à jour. Mais, c'est un SP cela dit aussi....

Je peux toujours bosser, c'est déjà l'essentiel...

Bonne journée.

A+

0 commentaire(s)
Classé sous : ,
Grâce à l'information que Thomas avait relayé il y a quelques temps, j'avais participé à cette conférence sur Silverlight animée par Christophe Lauer et Pierre Lagarde. Les vidéos, et ateliers (ceux du coach Silverlight) sont disponibles en ligne. Si vous ne connaissez pas Silverlight et que vous désirez débuter avec cette technologie, je vous le recommande vivement. Ils ont constitué un excellent point d'entrée pour moi. Smile
 
Projet Touareg (WebOS en Silverlight)
 
 
A +
1 commentaire(s)
Classé sous :
Christ Craft a pris l'initiative le 1er Juin dernier, de créer une application mobile windows par jour. Hier, il a fini la 30ième application. C'est une idée que j'ai trouvé originale, parce qu'il démontre ainsi qu'il n'est pas "si difficile" de créer des applications, surtout pour un pocket pc. Il y a un peu de tout (Applications utilisant le GPS, BlueTooth, etc...). C'est encourageant et ça donne des idées Big Smile !
 
Merci Chris.
 
Vu via Coding4Fun
Pour des raisons très pratiques en interne, nous nous sommes retrouvés à vouloir migrer l'ensemble de nos projets sous VSS vers SVN. Le processus de migration serait plus simple si nous ne voulions pas garder l'historique ainsi que les labels... Soit, en ayant un peu recherché sur google, je suis tombé sur cet outil : VSSMigrate. Il n'a pas été facile de l'utiliser, alors, je partage avec vous comment y parvenir Smile.

Quelques prérequis :

VSSMigrate a besoin d'un fichier .ini pour pouvoir parvenir à réaliser cette migration. Pour des raisons toujours pratiques, je l'ai appellé mon fichier de configuration VSSMigrate.ini.
Voici le contenu de ce fichier VSSMigrate.ini



[Settings]
VSSWIN32=C:\Progra~1\Mi53b4~1\
# VSS\Win32 directory which contains ss.exe
# J'Utilise le nom dos 8.3 car le programme n'aime pas les espaces .... (dir /x vous affiche le nom court!)

VSSDIR=\\VSSServer\VSSFolder
#VSS repository directory (contains srcsafe.ini)
#use environment variable SSDIR to set the current repository.
#PENSEZ à enregistrer la variable SSDIR dans les variables d'environnement avec la commande juste ci-dessous.
#set
SSDIR=\\VSSServer\VSSFolder\


VSSPROJ=$/YourProject/
#VSS project to start at (ie $/Product)

VSSUSER=myUserName
#User to use for VSS commands, use blank for none

VSSPASSWORD=myPassword
#password to use for VSS commands, blank is OK

SVNWIN32=C:\Progra~1\Subversion\bin
# SVN\bin directory which contains svn.exe

SVNUSER="first last"
#User to use for SVN commands, use blank for none, use quotes for two names

SVNPASSWORD=
#password to use for SVN commands, blank is OK

SVNURL=file:///C:/Repository/
#URL to use for the root of the check in

SVNPROJ=$/ProjectNameUnderSVN
#VSS project to start at (ie $/Product)

WORKDIR=c:\test
#Directory under which files and directories will be created as work progresses

DEBUG=1 #turn on debug output, blank is OK
AUTORETRY=0 #if a command fails to run, it will be run automatically 1 time before failing




Pour lancer VSSMigrate, il faut utiliser la ligne de commande VSSMigrate.exe fullPathToConfigFile

Je dis bien le chemin complet, sinon, ça ne passera pas.

Bon, après, il y a eu un "petit" souci que je n'ai pas voulu modifier dans les sources (qui sont aussi disponibles mais en C++ ;p). Pour chaque fichier récupéré depuis VSS, il faudra qu'il soit committé de nouveau vers SVN.
ça fonctionne bien, excepté qu'à certains moments, le programme prompte en nous demandant d'inscrire une nouvelle heure ...  Après avoir analysé un peu tout ça, j'ai remarqué que pour chaque fichier et version qu'il committait, il chantait la date système (en fonction de la date de version).  Il faudra donc penser aussi à la remettre comme elle était auparavant si elle s'est déréglée...


Si tout s'est bien passé, l'ensemble de vos sources sur VSS seront migrées sur votre SVN local. Je l'ai fait pour des raisons (encore) pratiques et à des buts de tests, car je ne savais pas si il y avait des chances que ça plante en cours...

Restera donc plus qu'à migrer de votre SVN local au Server SVN (tâche plus facile bien entendu Stick out tongue).

Bonne Migration et à bientôt !


3 commentaire(s)
Classé sous :
Un peu jaloux des possesseurs de XBOX (ou plutôt du fait qu'ils puissent avoir XNA pour développer des jeux), j'avais un peu cherché, il y a quelques mois, la même chose pour la WII (ouii, tout le monde n'a pas de XBOX...) sans vraiment trouver de choses. Et depuis hier, je vois un peu circuler l'information ( puis ) et je m'en fais relais Smile.
Unity (ou Unity3D), plateforme de développement de Jeux, permet la création de Jeux pour WII.

Encore mieux, cette plateforme supporte Javascript, C# et Boo.

Une solution que je pense probablement tester !

Bonne journée et a bientôt !
1 commentaire(s)
Classé sous : , ,

Il y a des outils que l'on ne devrait pas oublier ...

Aujourd'hui, alors que je me retrouvais à parser des fichiers Xml (avec .Net), je me suis retrouvé avec plusieurs problèmes dès le chargement justement de ce fichier Xml.

J'ouvre mon document dans mes navigateurs Web (Firefox, IE) (Bon, pas idéal pour parser, mais quand on a pas grand chose rapidement accessible sous la main), et il s'ouvre parfaitement bien. La seule chose (ou"hic") est que je n'ai pas d'encoding spécifié au début de mon fichier XML (il arrive dès fois que vous n'avez pas la main sur la création des fichiers que vous parsez...).

Soit, je me suis rappelé de cet outil que j'avais eu l'occasion d'utiliser il y a un moment sous Linux , puis sous Mac Os X: Xmllint qui est un simple et excellent parser XML en ligne de commande (et que l'on trouve aussi pour Windows!).

xmllint.exe --noout xmlFile vous permettra de vraiment parser votre fichier XML et de vérifier qu'il est vraiment valide et vraiment bien formé :).

xmllint

Bonne journée

0 commentaire(s)
Classé sous : ,
Généralement, lorsque nous utilisons des procédures stockées, et que nous avons besoin de récupérer certaines informations qu'elles nous renvoient, avec .NET, nous avons plusieurs possibilités.

Petit Rappel

La 1ère méthode, c'est en récupérant le paramètre de sortie de la procédure stockée (si elle en a ...)

Voici un exemple de procédure stockée:

create procedure dbo.TestProcedure (
    @outputParameter varchar(20) output
)

as
begin
    select @outputParameter = 'Hello World'
end


Et voici un exemple de code que nous pourrions utiliser pour récupérer le paramètre de type OutPutParameter.

//...
oComm.Connection = oConn;
oComm.CommandText = "TestProcedure";
oComm.CommandType = CommandType.StoredProcedure;

OleDbParameter p = new OleDbParameter("@OutputParameter", OleDbType.VarChar, 20);
p.Direction = ParameterDirection.Output;

oComm.Parameters.Add(p);
oComm.ExecuteNonQuery();

Console.WriteLine(p.Value.ToString());
//..


Ici, nous aurons d'affiché : Hello World


La 2ième méthode, presque similaire à la 1ière, c'est de récupérer son paramètre de retour (Attention ! Si vous utilisez plusieurs types de paramètres, le paramètre de retour doit être inséré en premier !!).

Ma procédure stockée modifiée :

create procedure dbo.TestProcedure (
    @outputParameter varchar(20) output
)

as
begin
  select @outputParameter = 'Hello World !'
    return 13
end


Un exemple de code que je pourrai utiliser pour récupérer les différents paramètres.

OleDbParameter p = new OleDbParameter("@OutputParameter", OleDbType.VarChar, 20);
p.Direction = ParameterDirection.Output;

OleDbParameter p1 = new OleDbParameter("@ReturnValue", OleDbType.Integer);
p1.Direction = ParameterDirection.ReturnValue;

oComm.Parameters.Add(p1); //On l'ajoute en premier dans la liste des paramètres
oComm.Parameters.Add(p);

int c = oComm.ExecuteNonQuery();

Console.WriteLine("Value Output {0}", p.Value.ToString());
Console.WriteLine("Value Return {0}", p1.Value.ToString());
Console.WriteLine(" c = {0}", c.ToString());


Nous aurons d'affiché :

Value Output Hello World !
Value Return 13
 c = -1


Ici, c renvoit -1, parce que je n'ai pas eu d'ordre DML d'executé dans ma procédure. Dans le cas contraire, j'aurai eu le nombre de ligne affectée par la dernière requête ...


Fin du Rappel


Aujourd'hui, j'ai eu à utiliser 2 méthodes différentes en fonction du contexte dans lequel j'étais. Je dispose de procédures stockées effectuant plusieurs traitements différents (des Update et des Deletes dans une même procédure ...).

Comment faire si je veux récupérer certains paramètres ou informations liées à l'éxécution de cette procédure ?

Pour résoudre cette question, j'ai utilisé 2 procédés.

La 1ère méthode, c'est me servir de la fonction print au sein de ma procédure stockée (J'utilise Sybase et le provider OleDb). La fonction print permettra de soulever des warnings que nous pourrons récupérer grâce à l'évènement InfoMessage associé à l'objet Connection.


Exemple

Ma Procedure :

create procedure TestProcedure
As
Begin
select * from matable
    print ' Warning!!! '
End



Le code que l'on pourrait utiliser pour récupérer le message renvoyé par print :

//..
OleDbConnection oConn = new OleDbConnection(m_connectionString)
oConn.Open();
oConn.InfoMessage += new OleDbInfoMessageEventHandler(oConn_InfoMessage);


static void oConn_InfoMessage(object sender, OleDbInfoMessageEventArgs e)
{
    Console.WriteLine("Info Message ...");
    foreach (OleDbError err in e.Errors)
    {
        Console.WriteLine(err.Source + " ### " + err.NativeError + " ### " + err.Message); }
    }
}

Toutes les fois ou un warning (avec la fonction print) ou autre message d'information sera renvoyé, nous pourrons l'afficher grâce à InfoMessage (et le delegate OleDbInfoMessageEventHandler).


La deuxième méthode que j'ai employée me permettait de faire une vérification des données que j'avais insérées à différents niveaux de traitement de ma procédure de stockée. Je trouve cette méthode un peu brutale dans la mesure ou les traitements ne sont pas dissociés (des updates, delete, et select dans la même procédure stockée). L'idéal est d'avoir une procédure stockée qui effectue le traitement, une autre qu permet de renvoyer les lignes parmi lesquels nous verrons les résultats attendus.


exemple:

Ma Procedure :

create procedure TestProcedure
As
Begin

    update data

    select someData to Check

    Other update

    select someOtherData To Check

    //etc...

End



J'ai créé une méthode simple pour afficher les données d'un datatable :

private static void DisplayDataSet(DataTable dt)
{
    foreach (DataRow row in dt.Rows)
    {
        foreach (DataColumn column in dt.Columns)
        {
            Console.Write(row[column].ToString() + " ");
        }
        Console.WriteLine();
    }
}



et je récupère l'ensemble des selects pour les afficher de cette façon :

DataSet ds = new DataSet();
OleDbDataAdapter adapter = new OleDbDataAdapter(oComm);
adapter.Fill(ds);

foreach (DataTable dt in ds.Tables)
{
    DisplayDataSet(dt);
}



Ce dernier cas était pratique pour moi parce que dans une même procédure je faisais plusieurs traitements, et j'affichais quelques lignes à certains niveaux de traitement. Ce n'est toutefois par quelque chose que je mettrais en production !

Voilà!

A + et Bonne journée !

Alors que je préparais une série d'articles (que je publierai bientôt), je me suis retrouvé face une petite problématique ... Faire un screenshot... ou plutôt, une série de screenshots, et les annoter.
Sous Windows, c'est facile ... Il y a une touche (Impr ecran) et mspaint ! Sous Tiger (Mac OS 10.4), il y a une combinaison de touches... (C'est possible de configurer et de changer, mais bon, un peu flemmard des fois..).

Après une petite recherche, j'ai découvert Skitch qui est vraiment magnifique et réponds facilement à toutes mes attentes!

Fonctionnalités de base pour un logiciel de capture (tirés du site) :

  • Snap a website
  • Capture a chat moment
  • Screenshot an application
  • iSight snap your bad hair day
  • Quickly sketch an idea
  • Tap into your iPhoto Library
  • Re-open images from your Skitch History

Et puis, fonctionnalités supplémentaires pour annoter et commenter :

  • Annotate with circles and gorgeous arrows
  • Add text that always stands out
  • Scribble and jot... it's automatically smoothed
  • Re-size and crop by simply dragging a corner
  • Drag the file to wherever you like, no need to save
  • Return to what you were doing — in seconds — not minutes!

Si vous souhaitez en savoir plus, il y a une petite vidéo ici

Bon, actuellement, il est en version beta et sur invitation (j'ai toujours une invitation si ça intéresse quelqu'un Big Smile ). Ah oui, c'est aussi sous Tiger... La version Windows ne devrait pas tarder ... Patience alors si ça vous a plu !


Si en plus des captures et annotations d'images vous souhaitez faire des screencasts, c'est possible avec (Jing Project) qui permet de faire la même chose (avec un peu moins de fonctionnalités que skitch pour les annotations sur les captures...). Juste en passant, Il a besoin du framework 3.0 pour fonctionner (WPF ??). J'essaierai la version Mac aussi pour voir !

Skitch

Jing Project

Les 2 softs sont gratuits ... (For Now...)

Voilà !

Bonne journée!

2 commentaire(s)
Classé sous : ,

Voilà un "petit" moment que je n'avais posté (depuis mon repos forcé en fait)...

De retour au boulot (qui m'avait un peu manqué, c'est vrai !), je me suis retrouvé face à un problème étrange qui m'a fait tourner en rond pendant bien 30 minutes... (Bon, allez, c'est vrai, 1 heure... Mais après le repas, l'après midi, sans pause café, on est toujours un peu fatigué)

Voici un exemple de code.

String query = "select field from table where condition1 = value1 and condition2 != null";
DataSet ds = ExecuteQuery(query);

foreach(DataRow dr in ds.Tables[0].Rows)
{
Console.WriteLine(dr["field"]);
}



Je testais ces quelques lignes dans une application en framework 1.1 (oui, il y en a qui tournent toujours jusqu'a présent ...). ExecuteQuery est une méthode qui exécute une  requête et renvoie  un dataset rempli.
 
La requête, exécutée directement sur la base de données (Sybase) fonctionne très bien, mais aucune ligne ne m'est renvoyée.. Je me pose des questions, et commence à mettre ça sur le dos de la version (je ne suis pas mauvaise langue, mais je veux migrer vite ! Big Smile ).

Je me mets à tester toutefois dans une application en 2.0. Même résultat... Je secoue un peu la tête, je vais me laver le visage. Je reviens.

Ne me demandez pas comment l'idée m'est venue, je décide de remplacer l'opérateur " != " par "is not".

!!! ça fonctionne...

Je ne sais pas si ça m'avait échappé, mais c'est une erreur ( erreur ?) que je n'avais jamais rencontré, ou même entendu parlé...

Qu'est ce que l'on est soulagé quand ça fonctionne, mais en même temps, un peu dégouté d'avoir perdu du temps sur un petit détail comme cela... La prochaine fois, on saura directement à peu près ce qu'il faudra tester...

A +

3 commentaire(s)
Classé sous : ,

Bonjour à tous,

Je ne parle pas souvent de ma vie privée, mais je ferai une petite exception. Voilà quelques temps que je n'ai pas posté et encore moins consulté tout ce qui est news ou encore moins utilisé un ordi et pour cause... Il y a quelques jours, j'ai été victime d'un accident de la voie publique. J'ai été renversé violemment par une voiture (sur un passage piéton, alors que j'étais en rollers)., voiture qui a pris la fuite... Je ne me souviens de rien (malheureusement?) et je m'en sors plutôt bien (amnésie totale des faits, traumastisme cranien, traumatisme genou gauche, fracture plancher orbite gauche, fracture des 2 sinus maxillaires, fracture malaire gauche, machoire déplacée, douleurs dorsales, nausées et etc... la liste est longue Big Smile). Je me suis rapidemment fait à la minerve, l'attelle, et les béquilles ! 2 semaines d'arret avec sorties non autorisées... Je parle mieux et je commence à faire quelques pas sans les béquilles !
Je ne peux pas toutefois rester longtemps assis ou debout... Le peu d'informations que j'ai pu voir circuler sur les blogs... ça avance vite la technologie en tout cas !

Bon, je retourne me reposer ... retour prévu d'ici une dizaine de jours ou plus.... Je garde bien entendu aussi mon sourire !

A bientôt, et un mot spécial à la technologie : "N'avance pas trop vite hein !"

Bonne journée

A +!

UPDATE : L'accident s'est passé à l'entrée du périphérique (pas très loin de la station service Esso, en face d'un immeuble) de Porte d'Ivry. Il parait qu'il y a des caméras qui ont pu filmé la scène, et je pense porter plainte contre X. Vous savez comment ça fonctionne et qui a la responsabilité de ces caméras? Savez vous combien de temps les enregistrements sont sauvegardés?

ps: Quand vous traversez une route, faites bien attention ! Même si c'est à vous de passer ...

17 commentaire(s)
Classé sous :

Je me fais relais d'un petit billet que j'ai vu ce matin.C'est vrai que j'avais eu l'occasion d'utiliser ce moteur de blogs (Subtext) et d'autres, et l'édition multiple de Tags ou de Catégories sur des billets déjà créés n'avait (n'est toujours pas) possible. Voici un outil (avec ses sources) permettant maintenant de le faire.

Blog Manager (L'auteur est toujours à la recherche d'un nom pour son application ...).

Bonne journée,

A +

0 commentaire(s)
Classé sous : ,

Je ne sais pas pour vous, mais il m'arrive souvent  de passer du temps à réfléchir sur l'optimisation des performances des applications que je peux développer. (Et pourtant, je n'utilise pas des outils spécialement liés à l'analyse de ces performances ...). Enfin, tout ça pour dire que, en développant une sorte d'Adapter ou de DBHelper (oui, je sais que ça existe déjà, mais, j'ai des besoins spécifiques ! Big Smile), je me suis intéressé à l'éxécution de plusieurs requêtes T-SQL groupées (des lots de 200 requêtes, 5000 requêtes, puis 10000 requêtes de suite), ainsi qu'à l'ouverture des connections vers la base de données. (Je travaille dans un contexte non multi-threadé ( Big Smile )).

Voici un exemple :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
Stopwatch watcher = new Stopwatch();
Console.WriteLine("Executing 200 queries with 200 Opening...");
watcher.Start();
for (int i = 0; i < 200; i++)
{
using (SqlConnection oConn = new SqlConnection(m_ConnectionString))
{
using (SqlCommand oComm = new SqlCommand())
{
String query = "INSERT INTO Users (FName, LName) VALUES ('F" + i.ToString() + "','L" + i.ToString() + "')";
oComm.CommandText = query;
oComm.Connection = oConn;
oConn.Open();
oComm.ExecuteNonQuery();
}
}
}

watcher.Stop();
Console.WriteLine("{0} ticks", watcher.ElapsedMilliseconds.ToString());
Console.WriteLine();

Console.WriteLine("Executing 200 queries with 1 Opening...");
using (SqlConnection oConn = new SqlConnection(m_ConnectionString))
{
using (SqlCommand oComm = new SqlCommand())
{
oConn.Open();
watcher.Reset();
watcher.Start();
for (int i = 0; i < 200; i++)
{
String query = "INSERT INTO Users (FName, LName) VALUES ('F" + i.ToString() + "','L" + i.ToString() + "')";
oComm.CommandText = query;
oComm.Connection = oConn;
oComm.ExecuteNonQuery();
}
watcher.Stop();
}
}
Console.WriteLine("{0} ticks", watcher.ElapsedMilliseconds.ToString());
Console.WriteLine();

 

Si je prends cet exemple et que je l'exécute pour 200, 5000, puis 10000 requêtes de suite, j'obtiens les résultats suivants :

Executing 200 queries with 200 Opening...
487 ticks

Executing 200 queries with 1 Opening...
247 ticks

Executing 5000 queries with 5000 Opening...
8249 ticks

Executing 5000 queries with 1 Opening...
6991 ticks

Executing 10000 queries with 10000 Opening...
21648 ticks

Executing 10000 queries with 1 Opening...
14148 ticks


Je pense que les résultats sont suffisamment explicites.. Et nous le savions déjà tous ...

Bon, je vais nettoyer ma table ... ça fait un moment que je fais les tests...

Bonne journée & A+ !

4 commentaire(s)
Classé sous : ,
Dans mon précédent billet, nous avions donc 4 procédures stockées.
Les procédures stockées managées ne contenaient aucune erreur (c'est le code "classique" de création de procédures de ce type !), ni meme les procédures stockées T-SQL.


CREATE PROCEDURE [dbo].[AddUsers]
@FirstName varchar(50),
@LastName varchar(50)
AS
BEGIN
SET NOCOUNT ON;

INSERT INTO dbo.Users (FirstName, LastName) VALUES (@FirstName, @LastName);
END


CREATE PROCEDURE [dbo].[GetUsers]
AS
BEGIN
SET NOCOUNT ON;

SELECT * FROM dbo.Users;
END


Ces dernières cependant avaient l'instruction SET NOCOUNT ON (généralement présente par défaut lors de la création d'une procédure stockée via les wizards de VS. SET NOCOUNT permet de désactiver ou pas le message permettant d'indiquer le nombre de lignes affectées(retournées) par une requête.

Concernant le programme en lui même, voici le premier bloc.

Console.WriteLine("Working with T-SQL Stored Procedures");

SqlParameter p1 = new SqlParameter("@FirstName", SqlDbType.VarChar);
p1.Value = "David";

SqlParameter p2 = new SqlParameter("@LastName", SqlDbType.VarChar);
p2.Value = "POULIN";

oComm.CommandText = "dbo.AddUsers";
oComm.Parameters.AddRange(new SqlParameter[]{p1, p2});
oComm.Connection = oConn;
oComm.CommandType = CommandType.StoredProcedure;

oConn.Open();

int count = 0;
count = oComm.ExecuteNonQuery();

Console.WriteLine("{0} rows inserted.", count);

using(SqlCommand oComm2 = new SqlCommand()) {
  oComm2.CommandText = "dbo.GetUsers";
  oComm2.Connection = oConn;

  using(SqlDataReader reader = oComm2.ExecuteReader())
  {
    while (reader.Read()) {
    Console.WriteLine("User : {0} {1}", reader["FirstName"], reader["LastName"]);
    }
  }
}


Il nous affiche :
Working with T-SQL Stored Procedures
-1 rows inserted
David POULIN

En effet, l'instruction SET NOCOUNT empêche d'afficher le nombre de lignes insérées dans notre table par notre procédure stockée !

Maintenant, le deuxième bloc dans lequel nous faisions appel à nos procédures stockées managées...
Les procédures stockées managées s'exécutent dans leur propre contexte par défaut. L'assembly contenant ces procédures stockées par défaut est définie sur des permissions de type SAFE. le type de connection autorisé uniquement est de type "Context Connection = True". En changeant le type de permissions, il est possible de pouvoir externaliser l'accès à d'autres ressources (définir sa propre chaine de connection), tout en utilisant Code Access Security .

Enfin, pour revenir à notre bloc de code, une exception de type InvalidOperationException sera levée à l'éxecution de la commande. Pourquoi ?
Il n'est pas possible tout simplement de pouvoir faire appel à des procédures stockées managées depuis C# (en tout cas, "par défaut" ...).

Rappelons le, l'idée est de pouvoir utiliser C# (VB aussi), d'utiliser ses propriétés, ses capacités et de créer des assemblies au sein de SQL Server 2005 !  Elles (les CLR SP) permettent de répondre à des problèmatiques qu'il n'est pas possible forcément (ou pas du tout) de résoudre avec du Transact SQL tout simplement.

Je n'en sais pas plus parce que, je n'ai pas cherché à en savoir plus pour le moment (étant donné les utilisations que j'en faisais) mais je vous redirige par ici ou il y a un excellent book d'initiation ou par la pour un overview de l'intégration CLR à SQL Server !

A +!

C'est un peu long, mais c'est une belle expérience que j'ai pu faire et je la partage. Et puis ce sont aussi de petites révisions sur ADO .NET, et les Procédures Stockées Managées.
Voici le contexte. J'ai 4 procédures stockées dont 2 sont des procédures stockées managées . Voici le code :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
using System;
using System.Data;
using System.Data.SqlClient;
using System.Data.SqlTypes;
using Microsoft.SqlServer.Server;


public class StoredProcedures
{
private static readonly string m_ContextConnection = @"Context Connection = True";

[SqlProcedure]
public static void GetUsersCLR()
{
using (SqlConnection oConn = new SqlConnection(m_ContextConnection))
{
using(SqlCommand oComm = new SqlCommand())
{
oComm.CommandText = "SELECT * FROM dbo.Users";
oConn.Open();
SqlContext.Pipe.ExecuteAndSend(oComm);
}
}
}

[SqlProcedure]
public static void AddUsersCLR(SqlString FirstName, SqlString LastName)
{
using (SqlConnection oConn = new SqlConnection(m_ContextConnection))
{
using (SqlCommand oComm = new SqlCommand())
{
SqlParameter p1 = new SqlParameter("@FirstName", SqlDbType.VarChar);
p1.Value = FirstName;
SqlParameter p2 = new SqlParameter("@LastName", SqlDbType.VarChar);
p2.Value = LastName;

oComm.CommandText = "INSERT INTO dbo.Users VALUES (@FirstName, @LastName)";
oComm.Parameters.AddRange(new SqlParameter[]{p1, p2});
oConn.Open();
SqlContext.Pipe.ExecuteAndSend(oComm);
}
}
}
};

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
CREATE PROCEDURE [dbo].[AddUsers]

  @FirstName varchar(50),
  @LastName varchar(50)
AS
BEGIN
  SET NOCOUNT ON;

  INSERT INTO dbo.Users (FirstName, LastName) VALUES (@FirstName, @LastName);

END

CREATE PROCEDURE [dbo].[GetUsers]

AS
BEGIN
  SET NOCOUNT ON;

  SELECT * FROM dbo.Users;

END

Que va t-il se passer lors de l'exécution de ce programme ?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
using(SqlConnection oConn = new SqlConnection(m_ConnectionString)) {
   
   using (SqlCommand oComm = new SqlCommand()) {
   Console.WriteLine("Working with T-SQL Stored Procedures");
      SqlParameter p1 = new SqlParameter("@FirstName", SqlDbType.VarChar);
p1.Value = "David";
SqlParameter p2 = new SqlParameter("@LastName", SqlDbType.VarChar);
p2.Value = "POULIN";

oComm.CommandText = "dbo.AddUsers";
oComm.Parameters.AddRange(new SqlParameter[]{p1, p2});
oComm.Connection = oConn;
oComm.CommandType = CommandType.StoredProcedure;
oConn.Open();

int count = 0;
count = oComm.ExecuteNonQuery();

Console.WriteLine("{0} rows inserted.", count);

      using(SqlCommand oComm2 = new SqlCommand()) {
   oComm2.CommandText = "dbo.GetUsers";
oComm2.Connection = oConn;
using(SqlDataReader reader = oComm2.ExecuteReader())
{
   while (reader.Read()) {
    Console.WriteLine("User : {0} {1}", reader["FirstName"], reader["LastName"]);
}
}
}
}
}


using (SqlConnection oConn = new SqlConnection(m_ConnectionString)) {
   using (SqlCommand oComm = new SqlCommand()) {
      Console.WriteLine("Working with CLR Stored Procedures");
      SqlParameter p1 = new SqlParameter("@FirstName", SqlDbType.VarChar);
p1.Value = "Toto";
SqlParameter p2 = new SqlParameter("@LastName", SqlDbType.VarChar);
p2.Value = "TATA";

oComm.CommandText = "dbo.AddUsersCLR";
oComm.Parameters.AddRange(new SqlParameter[] { p1, p2 });
oComm.CommandType = CommandType.StoredProcedure;
oComm.Connection = oConn;
oConn.Open();

      int count = 0;
      count = oComm.ExecuteNonQuery();

Console.WriteLine("{0} rows inserted.", count);
      using (SqlCommand oComm2 = new SqlCommand())
      {
         oComm2.CommandText = "dbo.GetUsersCLR";
oComm2.CommandType = CommandType.StoredProcedure;
oComm2.Connection = oConn;
using (SqlDataReader reader = oComm2.ExecuteReader())
{
   while (reader.Read()) {
   Console.WriteLine("User : {0} {1}", reader["FirstName"], reader["LastName"]);
}
}
}
   }
}

(Il n'y a pas d'erreur de parenthèses Big Smile !).

 

Je n'aime pas critiquer. Je n'aime pas non plus râler... Même quand le développeur du projet te fait des remarques... Mais il y a des jours ...

A mon arrivée dans ma nouvelle mission, je suis tombé sur un projet sur lequel je devais rajouter de nouvelles fonctionnalités. Projet (Win App) développé en c# (.NET 1.1, il y a tout juste un an... Tiens, la v2 du framework existait déjà pourtant...), 90% fonctionnel (quelques erreurs encore inexplicables...), un peu plus de 5000 lignes de code, rien de bien méchant quand je pouvais voir le mode de fonctionnement de l'application en elle même.

Bien, après avoir ouvert la solution ... je commençais un peu à changer d'avis...

  • Modèle 1 tier (quoique, des dossiers avaient été créés et dans lesquels il avait mis les formulaires, c'était bien organisé ... )
  • Des projets manquent à la solution (ProjetSetup ? Huh?)
  • Création d'un contrôle DataGridView (Le contrôle DataGrid a été surchargé et modifié de façon à ce qu'il se comporte comme le datagridview actuellement...)
  • etc...

C'était le premier coup d'oeil.. En entrant plus en profondeur dans l'application, j'ai vu des choses pouvant ressembler à ça :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
private string GetMonthShortName(DateTime dt)
    {
      string monthShortName = "";
      switch(dt.Month)
      {
        case 1:
          monthShortName = "Jan";
          break;
        case 2:
          monthShortName = "Feb";
          break;
        case 3:
          monthShortName = "Mar";
          break;
        case 4:
          monthShortName = "Apr";
          break;
        case 5:
          monthShortName = "May";
          break;
        case 6:
          monthShortName = "Jun";
          break;
        case 7:
          monthShortName = "Jul";
          break;
        case 8:
          monthShortName = "Aug";
          break;
        case 9:
          monthShortName = "Sep";
          break;
        case 10:
          monthShortName = "Oct";
          break;
        case 11:
          monthShortName = "Nov";
          break;
        case 12:
          monthShortName = "Dec";
          break;
      }

      return monthShortName;
    }

ou encore ça :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
private string FormatDate(DateTime dt)
    {
      string year = dt.Year.ToString();
      string month;
      string day;

      if(dt.Month < 10)
      {
        month = "0" + dt.Month;
      }
      else
      {
        month = dt.Month.ToString();
      }

      if(dt.Day < 10)
      {
        day = "0" + dt.Day.ToString();
      }
      else
      {
        day = dt.Day.ToString();
      }

      return year + month + day;
    }

Sans parler de tout ce qui est accès et manipulation de données ... :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public DataSet ExecuteQuery(String myQuery)
    {

      OleDbDataAdapter MyAdap = new OleDbDataAdapter(myQuery, myConnection);
      System.Data.DataSet MySet = new DataSet();

      try
      {
        MyAdap.Fill(MySet);
      }
      catch(OleDbException ae)
      {
        Console.WriteLine(ae.Message.ToString());
      }

      MyAdap.Dispose();
 
      return MySet;
    }

Je commençais un peu à comprendre les 5000 lignes..
Rajouter des fonctionnalités certes, mais dans quel esprit ?
J'ai du retoucher une grande partie de l'application pour l'optimiser (afin que la production continue de tourner), et l'adapter à du "code d'aujourd'hui".. Prochaine étape, la refaire en .NET 2.0 avec un modèle cohérent et plus "propre". Je ne serai pas ce que vous feriez à ma place, mais il y a des jours comme ça...

En passant, quelques liens vers quelques sites / articles intéressants :

et etc... Les ressources pour aider les développeurs sont bien nombreuses, et je pense que nous avons tout de notre côté pour nous aider à fournir un excellent travail.. Bon courage amis développeurs.

Bonne journée

ps:

J'oubliais. Je ne suis pas forcément meilleur, mais bon :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
private string GetMonthShortName(DateTime dt)    
{
  return dt.ToString("MMM", DateTimeFormatInfo.InvariantInfo);
}


private string FormatDate(DateTime dt)
    {

      return dt.ToString("yyyyMMdd");
    }


internal DataSet ExecuteQuery(String myQuery)
    {
      DataSet ds = new DataSet();

      try {
        using(OleDbConnection oConn = new OleDbConnection(m_ConnectionString))
        {
          oConn.Open();
          using(OleDbCommand oComm = new OleDbCommand(myQuery,oConn))
          {  
            using(OleDbDataAdapter adapter = new OleDbDataAdapter(oComm)) {
              adapter.Fill(ds);
            }
          }
        }
      }
      catch(Exception ae) { Console.WriteLine(ae.Message); }

      return ds;
    }

A +

Plus de Messages Page suivante »


Les 10 derniers blogs postés

- Office 365: Script PowerShell pour assigner des droits Full Control à un groupe défini par Blog Technique de Romelard Fabrice le 04-30-2017, 09:22

- SharePoint 20XX: Script PowerShell pour exporter en CSV toutes les listes d’une ferme pour auditer le contenu avant migration par Blog Technique de Romelard Fabrice le 03-28-2017, 17:53

- Les pièges de l’installation de Visual Studio 2017 par Blog de Jérémy Jeanson le 03-24-2017, 13:05

- UWP or not UWP sur Visual Studio 2015 ? par Blog de Jérémy Jeanson le 03-08-2017, 19:12

- Désinstallation de .net Core RC1 Update 1 ou SDK de Core 1 Preview 2 par Blog de Jérémy Jeanson le 03-07-2017, 19:29

- Office 365: Ajouter un utilisateur ou groupe dans la liste des Site collection Administrator d’un site SharePoint Online via PowerShell et CSOM par Blog Technique de Romelard Fabrice le 02-24-2017, 18:52

- Office 365: Comment créer une document library qui utilise les ContentTypeHub avec PowerShell et CSOM par Blog Technique de Romelard Fabrice le 02-22-2017, 17:06

- [TFS] Supprimer en masse les dépendances à SQL Enterprise ou Developer avant de procéder à une migration par Blog de Jérémy Jeanson le 02-20-2017, 20:30

- Office 365: Attention au volume utilisé par les fichiers de Thèmes de SharePoint Online par Blog Technique de Romelard Fabrice le 02-07-2017, 18:19

- [SCVMM] Supprimer une machine bloquée par Blog de Jérémy Jeanson le 01-31-2017, 21:22