Personnaliser les colonnes d'un DataGrid en Silverlight : 2ère partie#

Dans la première partie de cette série de posts nous avons vu comment définir nous même les colonnes d’un DataGrid en utilisant le DataGridTextColumn pour définir une colonne de type texte et le DataGridCheckBoxColumn pour définir une colonne de type CheckBox. Dans ce post nous allons voir comment utiliser DataGridTemplateColumn pour créer des colonnes complètement personnalisés.

Reprenons l’exemple que nous avons initié dans ce post et que nous avons enrichi dans celui-ci. Voici pour rappel le xaml du DataGrid

<Data:DataGrid x:Name="TasksGrid" AutoGenerateColumns="False" Style="{StaticResource MyGridStyle}" ItemsSource="{Binding Tasks}">
       <Data:DataGrid.Columns>
           <Data:DataGridTextColumn Binding="{Binding Name}" Header="Nom"></Data:DataGridTextColumn>
           <Data:DataGridCheckBoxColumn Binding="{Binding Realized}" Header="Réalisé"></Data:DataGridCheckBoxColumn>
       </Data:DataGrid.Columns>
</Data:DataGrid>

Et voici le résultat :

image

Nous allons maintenant rajouter deux colonnes à notre grille :

- La première contenant un voyant qui sera rouge si la tâche n’est pas encore réalisée et qui sera vert si la tâche est réalisée.

- La deuxième colonne va contenir un bouton qui permettra par la suite d’ouvrir une popup (Child window) pour visualiser la tâche

Pour ces deux colonnes nous allons utiliser DataGridTemplateColumn. C’est un type de colonne complètement personnalisable et qui, par défaut, possède un modèle vide. Nous pouvons alors y placer tous les contrôles que nous voulons, ces contrôles vont alors constituer notre colonne et se répéter pour chaque ligne liée à partir de la source de données.

Côté xaml nous allons utiliser la balise DataGridTemplateColumn dans la quelle nous pouvons définir entre autre le CellTemplate qui va contenir le modèle de cellule pour cette colonne en mode lecture. L’EditTemplate quand à lui peut être définit au même niveau que le CellTemplate et va contenir le modèle de cellule pour cette colonne lorsqu'elle sera en mode édition. Voici la définition d’une DataGridTemplateColumn avec un Cell Template et un EditTemplate vides.

<Data:DataGridTemplateColumn Header=""  IsReadOnly="True">
    <Data:DataGridTemplateColumn.CellEditingTemplate>
        <DataTemplate>
            
        </DataTemplate>
    </Data:DataGridTemplateColumn.CellEditingTemplate>
    <Data:DataGridTemplateColumn.CellTemplate>
        <DataTemplate>
        
        </DataTemplate>
    </Data:DataGridTemplateColumn.CellTemplate>
</Data:DataGridTemplateColumn>

Les templates vides comme définis ci-dessous lèvent une exception à l’exécution, il faut placer au moins un contrôle dans chaque DataTemplate.

La colonne voyant de réalisation

Nous allons tout d’abord enrichir notre classe Task en ajoutant une propriété que nous allons appeler RealizedColor, cette propriété va nous retourner la couleur verte si la valeur de la propriété Realized est à true et la couleur rouge le cas échéant.

public SolidColorBrush RealizedColor 
{
       get 
       {
           if (Realized)
               return new SolidColorBrush(Colors.Green);
           else
               return new SolidColorBrush(Colors.Red);
       }
}

Il suffit maintenant de rajouter une ellipse à notre template et de binder sa propriété Fill avec la propriété RealizedColor de notre classe task.

Positionnez vous en dessous de ces lignes et avant la fermeture de la balise DataGrid.Columns

<Data:DataGrid.Columns>
    <Data:DataGridTextColumn Binding="{Binding Name}" Header="Nom"></Data:DataGridTextColumn>
    <Data:DataGridCheckBoxColumn Binding="{Binding Realized}" Header="Réalisé"></Data:DataGridCheckBoxColumn>

et insérez le xaml suivant

<Data:DataGridTemplateColumn Header=""  IsReadOnly="True">
     <Data:DataGridTemplateColumn.CellTemplate>
         <DataTemplate x:Name="TemplateRealized">
             <Ellipse Fill="{Binding RealizedColor}" Width="15" Height="15"></Ellipse>
         </DataTemplate>
     </Data:DataGridTemplateColumn.CellTemplate>
</Data:DataGridTemplateColumn>

La colonne bouton de visualisation

Même principe pour cette colonne, sauf que nous allons juste rajouter un bouton, nous n’allons pas définir son fonctionnement pour un souci de simplification. Voici le xaml à insérer juste après celui que nous nous venons d’insérer :

<Data:DataGridTemplateColumn Header=""  IsReadOnly="True">
    <Data:DataGridTemplateColumn.CellTemplate>
        <DataTemplate x:Name="TemplateVisualiser">
            <Button x:Name="btnVisualiser" Content="Visualiser"></Button>
        </DataTemplate>
    </Data:DataGridTemplateColumn.CellTemplate>
</Data:DataGridTemplateColumn>

En exécutant, nous devrions avoir quelque chose qui ressemble à ça

image

Il reste encore le bouton visualiser qui ne fonctionne pas encore et pour cause nous n’avons pas défini l’EventHandler correspondant à l’évènement click du bouton. Pour cela, placez vous dans la balise bouton et commencez à taper le mot “click” l’IntelliSense va vous proposer l’évènement click, appuyez alors sur “=” et visual studio va vous proposer de créer pour vous l’EventHandler pour cet évènement, appuyez sur Entrée.

image

Si vous allez dans votre code behind vous allez trouver la méthode créée qui s’appelle btnVisualiser_Click (si vous avez nommé votre Bouton comme moi). Dans cette méthode nous allons instancier une ChildWindow, affecter l’objet Task à son DataContext et ouvrir la ChildWindow qui va afficher le contenu de l’objet Task contenu dans le DataContext.

Nous allons maintenant créer la ChildWindow qui va contenir le détail de la ligne. Positionnez vous au niveau du répertoire Views, click droit sur Views –> Add –> New Item –> Silverlight Child Window et nommez la Detail.xaml.

image

Pour afficher les données nous allons utiliser un DataForm (Composant du Silverlight Toolkit), voici tout le code xaml de la ChildWindow

<controls:ChildWindow xmlns:dataFormToolkit="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Data.DataForm.Toolkit"  x:Class="TasksManager.View.Views.Detail"
           xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
           xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
           xmlns:controls="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls"
           Width="400" Height="300" 
           Title="Detail">
    <Grid x:Name="LayoutRoot" Margin="2">
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto" />
            <RowDefinition Height="Auto" />
        </Grid.RowDefinitions>
        
        <dataFormToolkit:DataForm Grid.Row="0" AutoGenerateFields="True" AutoCommit="False" AutoEdit="False" x:Name="dataForm">
        </dataFormToolkit:DataForm>
    </Grid>
</controls:ChildWindow>

Dans le code behind de Detail.xaml, nous allons nous abonner à l’évènement “Loaded” et dans la méthode “Detail_Loaded” qui va être déclenchée après le chargement de la ChildWindow, nous allons remplir l’ItemSource de notre DataForm à l’aide du DataContext. Voici le code Behind de la Child Window :

    public partial class Detail : ChildWindow
    {
        public Detail()
        {
            InitializeComponent();
            this.Loaded += new RoutedEventHandler(Detail_Loaded);
        }

        void Detail_Loaded(object sender, RoutedEventArgs e)
        {
            List<Task> tasks = new List<Task>();
            tasks.Add(this.DataContext as Task);
            dataForm.ItemsSource = tasks;
        }

    }

Maintenant si vous exécutez votre code vous devriez voir les deux colonnes que nous avons rajouté

image

et si vous cliquez sur visualiser vous allez voir apparaître votre ChildWindow avec le détail de la ligne en cours.

image

L’utilisation du DataForm n’est pas vraiment appropriée ici puisque ce composant est fait pour gérer des collections d’objets mais pour les besoins de la démo cela suffit. Bien entendu, nous pouvons toujours aller plus loin en définissant des TemplateField pour le DataForm.

Voila voila, les TemplateColumn ne devraient plus avoir de secret pour vous au même titre que les ChildWindow dont l’utilisation se révèle très utile et surtout très simple. Dans le prochain post nous allons voir comment utiliser les Converter pour encore plus loin dans la personnalisation de votre DataGrid.

Wednesday, February 10, 2010 9:25:47 PM (Romance Standard Time, UTC+01:00) #    Comments [0]  | 

 

Personnaliser les colonnes d'un DataGrid en Silverlight : 1ère partie#

Nous avons vu dans ce post comment afficher des données provenant d’un service WCF dans un DataGrid. Nous avons utilisé pour cela la génération automatique de colonnes à partir de la source de données en utilisant l’attribut AutoGenerateColumns="True". Dans ce post nous allons partir de l’exemple précédemment réalisé pour personnaliser les colonnes de la grille.

Voici le code duquel nous allons partir :

<Data:DataGrid x:Name="TasksGrid" Style="{StaticResource MyGridStyle}" ItemsSource="{Binding Tasks}">
</Data:DataGrid>

L’attribut AutoGenerateColumns n’est pas spécifié parce que par défaut AutoGenerateColumns est positionné à True.

Pour rappel, voici la classe Task :

public class Task
{
        public int Id { get; set; }

        public string Name { get; set; }

        public bool Realized { get; set; }
}

Et en utilisant l’auto génération de colonnes voici ce qu’on obtient :

image

Supposons maintenant que l’on veut afficher uniquement la colonne Name et la colonne Realized uniquement. Pour cela nous allons tout d’abord mettre l’attribut AutoGenerateColumns du DataGrid à False et puis nous allons définir les colonnes contenues dans notre grille. Pour cela nous disposons de 3 types de colonnes :

- DataGridTextColumn : une colonne de type texte

- DataGridCheckBoxColumn : une colonne de type CheckBox

- DataGridTemplateColumn : une colonne personnalisée qui peut contenir tout ce qu’on veut

Pour notre exemple, nous allons utiliser le DataGridTextColumn pour la colonne Name et un DataGridCheckBoxColumn pour la colonne Realized.

Dans le fichier Tasks.xaml trouver cette balise DataGrid

<Data:DataGrid x:Name="TasksGrid" Style="{StaticResource MyGridStyle}" ItemsSource="{Binding Tasks}">
</Data:DataGrid>

et Remplacez le par cette balise

<Data:DataGrid x:Name="TasksGrid" AutoGenerateColumns="False" Style="{StaticResource MyGridStyle}" ItemsSource="{Binding Tasks}">
       <Data:DataGrid.Columns>
           <Data:DataGridTextColumn Binding="{Binding Name}" Header="Nom"></Data:DataGridTextColumn>
           <Data:DataGridCheckBoxColumn Binding="{Binding Realized}" Header="Réalisé"></Data:DataGridCheckBoxColumn>
       </Data:DataGrid.Columns>
</Data:DataGrid>

En définissant les colonnes de la DataGrid de cette manière, nous pouvons les personnaliser en définissant par exemple si ces colonnes peuvent être ordonnées en utilisant l’attribut “CanUserSort”. Nous pouvons aussi définir si nous pouvons redimensionner la colonne en utilisant l’attribut “CanUserResize”. Les colonnes deviennent personnalisables au maximum d’une manière très simple.

Sunday, January 17, 2010 2:54:00 PM (Romance Standard Time, UTC+01:00) #    Comments [0]  | 

 

Au coeur des Dependency Property / Attached Property#

Nous avons parlé dans un précédent post du databinding WPF/Silverlight en expliquant qu’est ce que le databinding, les Dependency Property, Attached Property et DataContext. Dans cet article nous allons nous intéresser plus particulièrement aux Dependency Property et aux Attached Property pour voir comment sont-ils implémentés.

Dependency Properties

Pour ce faire, nous allons commencer par créer une Dependency property, nous disposons pour cela du snippet VS, positionnez vous dans le code behind d’un fichier xaml et tapez propdp puis 2 fois sur la touche TAB.

Voici le code créé :

public int MyProperty
{
      get { return (int)GetValue(MyPropertyProperty); }
      set { SetValue(MyPropertyProperty, value); }
}

// Using a DependencyProperty as the backing store for MyProperty.  This enables animation, styling, binding, etc...
public static readonly DependencyProperty MyPropertyProperty =
        DependencyProperty.Register("MyProperty", typeof(int), typeof(MyType), new PropertyMetadata(MyPropertyChanged));

Dans le code créé par défaut par ce snippet, j’ai modifié deux petites partie : le type au quel on rajoute la Dependency Property (MyType) et une nouvelle instance de PropertyMetaData à la quelle on passe en paramètre le délégué qui traite le callback de l’évènement DependencyPropertyChanged (MyPropertyChanged). On s’arrête juste un petit instant sur ce point : On avait dit l’article précédent qu’une Dependency Property embarque des moyens de notification, à l’aide de cette méthode on va pouvoir interagir lorsque la propriété est modifiée. Voici la méthode MyPropertyChanged :

 static void MyPropertyChanged(DependencyObject obj,
                        DependencyPropertyChangedEventArgs args)
{

 }

Le premier argument est l’instance de la classe dont la propriété a été modifiée. Le deuxième argument est un objet de type DependencyPropertyChangedEventArgs qui nous met à disposition des données précieuses

image 

NewValue : Valeur de la propriété après modification (de type object)

OldValue : Valeur de la propriété avant modification (de type object)

Property : L’instance de  la Dependency Property

Dans la déclaration le la Dependency Property, nous distinguons 2 parties :

- Une propriété CLR (MyProperty), nous remarquons les méthodes GetValue et SetValue respectivement utilisées dans le Set et le Get de la propriété. Ces deux méthodes sont définies au niveau de Dependency Object c’est pour cela que la classe qui définit une Dependency Property doit être un Dependency Object. Elles servent à récupérer / mettre à jour la valeur effective de la Dependency Property. La valeur n’est donc pas stockée au niveau de l’objet qui définit la Dependency property.

- Une Dependency Property qui, comme l’indique si bien le commentaire généré, fait office de stockage pour la propriété CLR MyProperty. Intéressons nous maintenant à la méthode statique Register de la classe DependencyProperty. Si on fait un petit Reflector sur WindowsBase.dll –> System.Windows –> Dependency Property on trouve la méthode Register. Et quelques appels de méthodes plus loin on se retrouve dans la méthode RegisterCommon qui  nous intéresse. Au début de cette méthode nous retrouvons ces quelques lignes de code :

image

La classe FromKeyName est instanciée pour avoir la clé Key à partir du nom de la propriété et du type qui définit la Dependency Property, ensuite la clé résultat est utilisée pour voir si cette elle est contenue dans PropertyFromName et si c’est le cas on dit que la propriété existe déjà. Donc ce qu’on peut en tirer pour l’instant c’est que PropertyFromName contient toutes les clés des Dependency Properties. Si on regarde PropertyFromName on s’aperçoit que c’est une Hashtable, nous savons qu’une Hashtable est une collection de pairs clé / valeur. Donc on connait déjà la clé qui est la clé des Dependency property de type FromKeyName, il nous manque uniquement la valeur.

Poursuivons maintenant la lecture de la méthode, pour trouver plus loin le stockage de la Dependency Property à l’aide de cette ligne :

image

Où dp est la Dependency Property.

Le stockage des Dependency Properties se fait donc tout simplement dans une Hashtable où la clé est de type FromNameKey et la valeur est la Dependency Property.

Attached Properties

Regardons maintenant comment ça se passe pour une Attached Property. Le snippet VS pour créer une Attached Property est propa voici le code généré :

 

public static int GetTestProperty(DependencyObject obj)
{
      return (int)obj.GetValue(TestPropertyProperty);
}

public static void SetTestProperty(DependencyObject obj, int value)
{
      obj.SetValue(TestPropertyProperty, value);
}

// Using a DependencyProperty as the backing store for TestProperty.  This enables animation, styling, binding, etc...
public static readonly DependencyProperty TestPropertyProperty =
            DependencyProperty.RegisterAttached("TestProperty", typeof(int), typeof(Button), new PropertyMetadata(MyPropertyChanged));

Nous remarquons deux différences :

- Le getter / setter sont deux fonctions statiques au lieu d’une propriété dans le cas d’une Dependency Property. C’est grâce à ça qu’on peut bénéficier des Attached Properties sur tous les objets.

- On utilise la méthode RegisterAttached au lieu de Register. En creusant un peu avec Reflector WindowsBase.dll –> System.Windows –> Dependency Property on trouve la méthode RegisterAttached qui appelle une autre méthode appelée RegisterAttached qui appelle elle même la méthode RegisterCommon appelée lors du Register d’une Dependency Property. On retombe donc sur nos pieds!!!

image

Le stockage d’une Attached Property se fait de la même manière que pour une Dependency Property c’est à dire dans une Hashtable.

c# | c#3 | Silverlight | Silverlight 3 | WPF
Thursday, November 26, 2009 10:49:42 AM (Romance Standard Time, UTC+01:00) #    Comments [0]  | 

 

Databinding : Dependency property, Attached property, DataContext#

Le data binding lie un composant UI qui hérite de System.Winwdows.FrameworkElement à une source de données, pour cela il y a deux conditions fondamentales :

- Il faut que le composant cible du data binding soit un FrameworkElement

- Il faut que la propriété cible soit une DependencyProperty

Nous avons vu dans ce post comment on arrive à lier une grille à une source de données, pour rappel nous avons utilisé cette ligne de code :

<Data:DataGrid x:Name="TasksGrid" ItemsSource="{Binding Tasks}">
</Data:DataGrid>

A voir cette ligne, nous pouvons penser que c’est magique, pour comprendre comment ça marche derrière, nous allons écrire la même chose par code. Ça donne ça :

TasksViewModel tasksvm = new TasksViewModel();

Binding bind = new Binding("Tasks");
TasksGrid.SetBinding(DataGrid.ItemsSourceProperty, bind); 

//Partie existante
this.DataContext = tasksvm;
tasksvm.FillTasks();

Si nous traduisons les 2 lignes qui nous intéressent, nous commençons par instancier un objet de type Binding en lui donnant en paramètre le nom de la propriété du type de l’objet au quel la grille va être liée. Ensuite nous disons à la grille qu’elle doit utiliser l’objet bind de type Binding pour la liaison de sa propriété ItemSource. Donc en fait ce que nous pouvons en tirer c’est que la liaison entre la grille et la source de données ne se fait pas automatiquement mais qu’il y a un objet de type Binding qui régit cette liaison. Cet objet va notifier le moteur de binding à chaque fois qu’il y a un changement dans la source de données, ce moteur va mettre à jour la Dependency property correspondante (ItemSource dans notre exemple) et va ordonner un rafraîchissement du composant graphique.

Depenedency property

La dependency property est une propriété .NET étendue qui embarque des moyens de notification et de liaison avec une source de données et dont la valeur n’est connue qu’à l’exécution (Runtime). Elle peut enrichir n’importe quelle classe du moment que cette dernière est un Dependency object mais elle est majoritairement utilisée dans les composants graphiques.

DataContext

Le DataContext est une propriété que l’on retrouve dans tous les contrôles bindable à une source de données. Elle est héritée du contrôle parent et peut être redéfinie au niveau de l’enfant. Elle est utilisée essentiellement pour le databinding, elle représente la source de données et contient en général une instance d’une entité. Si on reprend notre exemple ci-dessus :

<Data:DataGrid x:Name="TasksGrid" ItemsSource="{Binding Tasks}">

Avec {Binding Tasks} on dit que la propriété ItemsSource du datagrid est une propriété liée à la propriété Tasks de l’objet de liaison, mais où est ce qu’on définit l’objet de liaison? En effet, l’objet de liaison n’est rien d’autre que le DataContext, donc il suffit de peupler le DataContext avec l’objet contenant la propriété Tasks et le moteur de binding ira chercher les données de cet emplacement lorsqu’il le faudra.

Attached Property

Une Attached Property c’est le même principe qu’une Dependency Property à une exception près : Tous les Dependency Object peuvent bénéficier d’une Attached Property. Vous allez me dire, mais à quoi ça sert ??? En effet, dans certains cas, une propriété d’un élément graphique ne prend sens que dans un certain contexte, donc pour éviter que tous les éléments graphiques embarquent toutes les propriétés possibles et imaginables dans tous les différents contextes qui existent, les attached property ont été créés . C’est encore flou :) Prenons un exemple : considérons un DockPanel qui contient des éléments graphiques.

<DockPanel>
         <TextBox DockPanel.Dock="Left"/>
</DockPanel>

Le contrôle TextBox ne possède pas la propriété Dock mais DockPanel possède une AttachedProperty appelée Dock et comme on a dit que tous les éléments peuvent bénéficier d’une Attached Property, tous les éléments graphiques peuvent utiliser cette propriété, c’est ce qui fait que TextBox peut renseigner DockPanel.Dock à Left. La propriété Dock ne prend sens pour un TextBox que lorsque celui-ci est contenu dans un DockPanel et son utilisation hors de ce contexte est obselète. Une question qui nous vient à l’esprit tout de suite : mais si la propriété est appelée dans un mauvais contexte ? Si le contexte d’appel est inapproprié (une textbox qui n’est pas contenue dans un DockPanel et qui renseigne DockPanel.Dock) la propriété est ignorée.

Comme les Dependency Property, les Attached Property bénéficient de l’héritage hierarchique, elle peut donc être définie au sein d’un élément parent pour être répercutée sur tous ses éléments enfants comme elle peut être redéfinie au niveau d’un élément enfant.

Conclusion

Les concepts énoncés dans cet article sont fondamentaux pour toute personne voulant travailler avec des technologies comme Silvertlight ou WPF. Mais comment ça marche derrière ? Comment sont stockés les valeurs renseignées pour les Attached Property ? C’est ce que nous allons voir dans un prochain post.

c# | c#3 | Silverlight | Silverlight 3 | WPF
Wednesday, November 18, 2009 11:44:40 AM (Romance Standard Time, UTC+01:00) #    Comments [0]  | 

 

Silverlight : afficher des données provenant d’un service WCF dans une grille DataGrid#

Le titre de l’article est assez explicite, on va afficher des données provenant d’un service WCF dans un DataGrid. Cet article va servir d’introduction pour la suite des articles autour de Silverlight.

Les données

On va créer une table Task qui va contenir des tâches, voici le script :

create table Task(id int IDENTITY, 
                [name] varchar(255), 
                comment varchar(1000), 
                realizationDate DateTime, 
                termDate DateTime, 
                reminderDate DateTime,
                concerned varchar(100),
                realized bit default 0
                )

Le service WCF

On va créer un service WCF et à l’aide d’entity framework (pour un souci de rapidité) on va chercher la liste des tâches dans la base de données et la renvoyer.

Créer un nouveau projet « WCF Service Project » et nommez le NavigationAppServices. Nommez la solution que vous allez créer « NavigationApp ».

Dans ce projet créez un nouveau « WCF Service » et nommez le « TasksService », et dans ce projet, créez un répertoire DataAccess.

Dans ce répertoire créer un nouveau « ADO.NET entity data model » et nommez le « DbModel ». N’oubliez pas de cocher la table Task qu’on vient de créer. Ceci va vous générer le modèle Entity Framework avec l’entité Task. (Si vous avez besoin de plus de détails sur cette partie je vous recommande ce post)

Revenons maintenant à notre service TasksService et rajoutons le using et la méthode suivante :

using NavigationAppServices.DataAccess;

public List<Task> GetList() 
{
      DbEntities entities = new DbEntities();
      return entities.Task.ToList<Task>();
}

Dans l’interface ITasksService n’oubliez pas de déclarer la méthode comme suit:

[OperationContract]
List<Task> GetList();

Alors juste une petite mise en garde, ce service que nous avons créé en 30 secondes ne respecte aucun best practice du développement N-Tiers mais pour le besoin de cet article ça suffit amplement.

L’application Silverlight

On y est, maintenant qu’on a préparé le terrain, on va pouvoir accéder à ce service WCF et afficher la liste des tâches dans une grille. Pour ce faire, dans la même solution qui contient les services, créez tout d’abord un projet de type « Silverlight navigation application » et nommez le « NavigationApp ». (Pour comprendre la coquille vide que vous génère Visual studio dans ce cas reportez vous à ce post)

Visual studio va alors nous créer une coquille vide. On va ignorer cette coquille et garder uniquement le point d’entrée de notre application qui est App.xaml pour construire notre application Silverlight.

Positionnez vous au niveau du projet NavigationApp et créez un nouveau « Silverlight User Control » en le nommant MasterPage.xaml ( Nous l’avons nommé comme cela juste parce que ça sera le conteneur de notre application Silverlight mais le fonctionnement n’est pas du tout pareil)

image

Dans le fichier App.xaml.cs remplacez la ligne suivante :

this.RootVisual = new MainPage();

par celle la :

this.RootVisual = new MasterPage();

L’application va donc au démarrage charger notre conteneur au lieu de charger le conteneur généré par Visual Studio. Si vous lancez l’application à ce stage vous aurez une page blanche puisque MasterPage ne contient rien.

Nous allons maintenant travailler sur l’aspect général de l’application, pour cela nous allons définir une grille qui nous permettra d’organiser un peu l’interface et positionner nos différents éléments graphique. Ouvrez MasterPage.xaml et en dessous de cette ligne :

<Grid x:Name="LayoutRoot" Background="White">

Placez ces quelques lignes qui permettent de définir la grille

<Grid.RowDefinitions>
      <RowDefinition Height="50"></RowDefinition>
      <RowDefinition Height="*"></RowDefinition>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
      <ColumnDefinition Width="100"></ColumnDefinition>
      <ColumnDefinition Width="*"></ColumnDefinition>
</Grid.ColumnDefinitions>

Rien de sorcier, nous avons définit une grille avec 2 lignes et 2 colonnes.

Nous allons maintenant remplir les cases de la grille avec des éléments graphiques.

<Border Style="{StaticResource MyBannerStyle}">
      <TextBlock Text="Mon application de la mort"></TextBlock>
</Border>
<Border Style="{StaticResource MyToolboxStyle}">
</Border>
<Border Style="{StaticResource MyBodyPageStyle}">
</Border>

Border est un conteneur graphique permettant de regrouper d’autres éléments. Donc on a définit 3 éléments de type border :

- Le premier sera notre bannière et occupera toute la première ligne de la grille

- Le deuxième sera un panel à gauche de l’écran qui restera vide jusqu’à la fin de cet article mais qui est destiné à accueillir une boite à outils ou un menu.

- Le troisième et dernier va accueillir notre application Silverlight

Les styles qui sont appliqués à ces éléments graphiques sont les suivants :

<Style x:Key="MyBannerStyle" TargetType="Border">
        <Setter Property="Background" Value="#C0C0C0"></Setter>
        <Setter Property="Grid.Row" Value="0"></Setter>
        <Setter Property="Grid.Column" Value="0"></Setter>
        <Setter Property="Grid.ColumnSpan" Value="2"></Setter>
</Style>
<Style x:Key="MyToolboxStyle" TargetType="Border">
     <Setter Property="Background" Value="#C0C0C0"></Setter>
     <Setter Property="Grid.Row" Value="1"></Setter>
     <Setter Property="Grid.Column" Value="0"></Setter>
</Style>

<Style x:Key="MyBodyPageStyle" TargetType="Border">
     <Setter Property="Grid.Row" Value="1"></Setter>
     <Setter Property="Grid.Column" Value="1"></Setter>
</Style>

Il faut les rajouter dans /Assets/Styles.xaml.

Dans MasterPage.xaml , positionnez vous maintenant en dessous de cette ligne.

<Border Style="{StaticResource MyBodyPageStyle}">

et rajoutez le code suivant :

<navigation:Frame x:Name="ContentFrame" Style="{StaticResource ContentFrameStyle}" Source="/Tasks">                          
                <navigation:Frame.UriMapper>
                    <uriMapper:UriMapper>
                        <uriMapper:UriMapping Uri="" MappedUri="/Views/Tasks.xaml"/>
                        <uriMapper:UriMapping Uri="/{pageName}" MappedUri="/Views/{pageName}.xaml"/>
                    </uriMapper:UriMapper>
                </navigation:Frame.UriMapper>
</navigation:Frame>

Pas de panique, il vous manque des briques, positionnez vous en dessous de cette ligne

xmlns:x=http://schemas.microsoft.com/winfx/2006/xaml

et rajoutez les 2 lignes suivantes :

xmlns:uriMapper="clr-namespace:System.Windows.Navigation;assembly=System.Windows.Controls.Navigation"
xmlns:navigation="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Navigation"

Ce code définit la zone qui va accueillir les pages Silverlight. (Pour en savoir plus à propos de l’uri mapping je vous recommande ce post

Créons maintenant notre page Tasks, positionnez vous au niveau du répertoire Views et ajouter une nouvelle « Silverlight page » et nommez la Tasks.xaml.

image

Dans Tasks.xaml, on va tout d’abord définir une Grille pour placer la DataGrid et un petit bouton de rafraîchissement. La grille aura 1 ligne et 2 colonnes, voici son code :

<Grid.RowDefinitions>
    <RowDefinition Height="*"></RowDefinition>
    <RowDefinition Height="30"></RowDefinition>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
     <ColumnDefinition Width="*"></ColumnDefinition>
</Grid.ColumnDefinitions>

Avant de créer la DataGrid qui va contenir la liste des tâches, allons déjà récupérer la liste des tâches à partir de notre service web, on va essayer de faire les choses à peu près proprement avec le pattern MVVM, on ne va pas s’attarder sur ce pattern qui fera l’objet d’un prochain post mais sachez juste que si vous vous intéressez à Silverlight ou wpf, le pattern MVVM est incontournable.

Dans le projet NavigationApp créez 2 nouveaux répertoires et nommez les « Model » et « ViewModel ».

Dans le répertoire Model, créez une nouvelle classe et nommez la « Task.cs », voici son contenu :

public class Task
{
        public int Id { get; set; }

        public string Name { get; set; }

        public bool Realized { get; set; }
}

Donc on a dans notre Model une classe Task qui représente une petite partie de la classe Task exposée par le service.

Dans le répertoire ViewModel, créez une nouvelle classe et nommez-la « TasksViewModel.cs ». Cette classe va contenir les propriétés sur les quels on va lier les composants graphiques de l’interface TasksViewModel. Pour pouvoir notifier les composants graphiques que le contenu des propriétés a changé il faut que notre classe implémente l’interface INotifyPropertyChanged. Pour cela il faut déclarer dans la classe le delegate PropertyChanged de cette manière :

public event PropertyChangedEventHandler PropertyChanged;

On a rajouté la méthode RaisePropertyChanged permettant de déclancher l’évènement PropertyChanged sur une propriété.

Notre grille va se lier à une liste de tâches, donc on va rajouter cette propriété :

private List<Task> tasks;
public List<Task> Tasks 
{
       get { return tasks; }
       set 
       {
            tasks = value;
            RaisePropertyChanged("Tasks");
       }
}

Nous remarquons bien que dans le set on déclanche l’évènement PropertyChanged dès qu’on a modifié le contenu de la propriété.

Et enfin on va aussi rajouter la méthode qui va contacter le service pour ramener les données (FillTasks) et le délégé (taskServiceClient_GetListCompleted) qui va traîter la réponse asynchrone du service et qui va modifier le contenu de la liste Tasks.

Au final voici le code de la classe TasksViewModel :

    public class TasksViewModel : INotifyPropertyChanged
    {
        #region INotifyPropertyChanged Members

        public event PropertyChangedEventHandler PropertyChanged;

        #endregion

        private void RaisePropertyChanged(string propertyName)
        {
            if (this.PropertyChanged != null)
            {
                this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }

        public void FillTasks() 
        {
            Services.Tasks.TasksServiceClient taskServiceClient = new NavigationApp.Services.Tasks.TasksServiceClient();
            taskServiceClient.GetListCompleted += new EventHandler<NavigationApp.Services.Tasks.GetListCompletedEventArgs>(taskServiceClient_GetListCompleted);
            taskServiceClient.GetListAsync();
        }

        void taskServiceClient_GetListCompleted(object sender, NavigationApp.Services.Tasks.GetListCompletedEventArgs e)
        {
            List<Task> resultTasks = new List<Task>();

            foreach(NavigationApp.Services.Tasks.Task t in e.Result)
            {
                Task mt = new Task();
                mt.Id = t.id;
                mt.Name = t.name;
                if (t.realized.HasValue)
                    mt.Realized = t.realized.Value;
                resultTasks.Add(mt);
            }

            this.Tasks = resultTasks;
        }

        private List<Task> tasks;

        public List<Task> Tasks 
        {
            get { return tasks; }
            set 
            {
                tasks = value;
                RaisePropertyChanged("Tasks");
            }
        }
    }

Il ne nous reste qu’à ajouter la grille et le bouton à l’interface dans Tasks.xaml

<Data:DataGrid x:Name="TasksGrid" Style="{StaticResource MyGridStyle}" ItemsSource="{Binding Tasks}">
</Data:DataGrid>
<Button x:Name="btnRefreshGrid" Click="btnRefreshGrid_Click" Style="{StaticResource MyButtonStyle}" Content="Refresh">
</Button>

Concentrons nous un peu sur le bout de code de la grille et plus particulièrement sur la valeur de l’attribut ItemSource {Binding Tasks}. Cette valeur permet de lier notre grille à l’attribut Tasks de l’objet du DataContext courant. Le DataContext est une propriété de type à la quelle on peut lier (Binding) des composants graphiques, qui contient en général une instance de l’objet qui va peupler nos composants graphiques et qui est hérité par tous les composants graphiques enfants tant qu’il n’est pas redéfini au niveau du composant en lui-même. Nous aurons l’occasion de revenir dessus plus en détail dans un prochain post.

Ajouter les styles suivants au niveau de Styles.xaml :

<Style x:Key="MyGridStyle" TargetType="Data:DataGrid">
        <Setter Property="Grid.Row" Value="0"></Setter>
        <Setter Property="Grid.Column" Value="0"></Setter>
</Style>
<Style x:Key="MyButtonStyle" TargetType="Button">
        <Setter Property="HorizontalAlignment" Value="Left"></Setter>
        <Setter Property="MaxWidth" Value="100"></Setter>
        <Setter Property="Grid.Row" Value="1"></Setter>
        <Setter Property="Grid.Column" Value="0"></Setter>
</Style>

Ajouter l’espace de noms System.Windows.Controls au niveau de Styles.xaml et Tasks.xaml

xmlns:Data="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Data"

Dans Tasks.xaml.cs, positionnez vous dans le constructeur et ajouter le code suivant :

TasksViewModel tasksvm = new TasksViewModel();
this.DataContext = tasksvm;
tasksvm.FillTasks();

Tout ce qu’on fait ici c’est dire que le DataContext de la classe Tasks est une nouvelle instance de notre ViewModel et on rempli la propriété Tasks. Vu qu’on a lié notre grille à la proprété Task, lorsque cette dernière sera remplie et donc modifiée elle déclanchera l’évènement PropertyChanged et le moteur de binding va rafraîchir le contenu de la grille.

Et enfin, rajouter cette méthode dans la classe Tasks dans Tasks.xaml.cs qui va s’exécuter lors du click du bouton pour rafraîchir le contenu de la grille.

private void btnRefreshGrid_Click(object sender, RoutedEventArgs e)
{
        ((TasksViewModel)this.DataContext).FillTasks();
}

Vous pouvez maintenant éxecuter, vous devriez avoir quelque chose qui ressemble à ça (n’oubliez pas de remplir la base J)

Vous pouvez aussi modifier le contenu de la table et cliquer sur le bouton rafraîchir qui va mettre à jour la propriété Tasks, cette mise à jour va être notifiée au moteur de binding qui va redessiner la grille.

image

Une dernière explication par rapport à la grille, vous avez certainement remarqué qu’il y a bien 3 colonnes qui correspondent bien aux 3 proriétés de la classe Task. Mais ce qu’il faut surtout remarquer c’est que la colonnes « Realized » a été automatiquement dessinée sous forme de checkbox. En effet, par défaut le DataGrid s’adapte au type de la donnée qu’il affiche et ceci en associant à chaque type un DataTemplate par défaut.

Conclusion

A travers cet exemple basqiue on a introduit plusieurs notions sur les quels on va revenir plus en détail dans de prochains postes :

- Le DataContext et le databinding

- Le pattern MVVM

- Le DataGrid

- Les styles

A très bientôt ;)

Monday, October 26, 2009 10:57:48 AM (Romance Standard Time, UTC+01:00) #    Comments [2]  | 

 

Silverlight 3 : analyse d’un projet « Silverlight navigation application »#

Pour ceux qui se demandent comment marche Silverlight et par quel bout prendre la chose, cet article est fait pour vous. Dans cet article on va analyser la coquille vide créée par Visual Studio pour bien comprendre les mécanismes mis en évidence dans le projet exemple.

Nous allons commencer par créer un projet de type « Silverlight navigation application » (Si vous avez besoin d’aide, reportez vous à ce post)

Concentrons nous maintenant sur la solution générée, qui doit avoir cette allure :

image

Nous avons donc 2 projets :

- NavigationApp.web : application web qui va contenir notre application Silverlight.

- NavigationApp : notre application silverlight

NavigationApp.web

Le projet est une application web, nous remarquons les deux pages de tests suivantes : NavigationAppTestPage.aspx NavigationAppTestPage.html. Ces deux pages mettent en œuvre l’hébergement de l’application Silverlight NavigationApp.xap en se basant sur le fichier Silverlight.js. Si on ouvre la page html (ou aspx) on va trouver essentiellement la balise Object qui va nous permettre d’inclure notre application Silverlight dans la page.

<object data="data:application/x-silverlight-2," type="application/x-silverlight-2" width="100%" height="100%">
          <param name="source" value="ClientBin/NavigationApp.xap"/>
          <param name="onError" value="onSilverlightError" />
          <param name="background" value="white" />
          <param name="minRuntimeVersion" value="3.0.40818.0" />
          <param name="autoUpgrade" value="true" />
          <a href="http://go.microsoft.com/fwlink/?LinkID=149156&v=3.0.40818.0" style="text-decoration:none">
               <img src="http://go.microsoft.com/fwlink/?LinkId=108181" alt="Get Microsoft Silverlight" style="border-style:none"/>
          </a>
        </object>

Dans la balise object, nous pouvons définir plusieurs paramètres dont :

- Source : qui est le binaire à télécharger.

- onError : la fonction javascript à executer lorsqu’une exception non catchée est levée au sein de l’application Silverlight.

- backgroung : la couleur de fond de l’application silverlight.

- minRuntimeVersion : La version minimale du plugin silverlight pouvant exécuter le binaire.

- autoUpgrade : Définit une valeur qui indique si une version du plug-in Silverlight antérieure au minRuntimeVersion spécifié tente de se mettre à jour automatiquement.

La liste complète des paramètres que nous pouvons définir pour une balise object est ici, ce qu’il faut retenir c’est que dès que nous voulons changer quelque chose par rapport à l’intégration de l’application ou du plugin silverlight au sein de la page c’est de ce côté ci qu’il faut regarder :)

L’iframe « _sl_historyFrame » est utilisée pour gérer l’histoique de navigation au sein de l’application Silverlight. Elle est partagée par toutes les applications Silverlight dans une même page.

Vous avez certainement remarqué que le fichier Silverlight.js est inclus dans ces pages de test. Ce fichier permet principalement de faire 2 types de fonctionnalités :

- Inclure programmatiquement une application Silverlight.

- Améliorer la procédure d'installation ou de mise à niveau standard de Silverlight.

Vous trouverez ici la liste des fonctions que propose ce fichier js.

Je pense qu’on a fait le tour de l’application web, tournons nous maintenant vers l’application Silverlight.

NavigationApp

C’est le projet qui contient notre application silverlight. Le point d’entrée de cette application est le fichier App.xaml, donc commençons déjà par analyser ce fichier. Son contenu devrait ressembler à ça :

<Application   
  x:Class="NavigationApp.App"
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">

  <Application.Resources>
    <ResourceDictionary>
      <ResourceDictionary.MergedDictionaries>
        <ResourceDictionary Source="Assets/Styles.xaml"/>
      </ResourceDictionary.MergedDictionaries>
    </ResourceDictionary>
  </Application.Resources>

</Application>

On voit tout de suite que c’est une application avec la balise <Application>. Dans ce fichier on va définir toutes les ressources au niveau de l’application, ici à titre d’exemple, la ressource est un fichier xaml contenant tous les styles de l’application.

Dans le code behind de l’App.xaml on s’abonne aux évènements « Startup » et « UnhandledException » en définissant le comportement de l’application lorsque ces deux évènements sont déclanchés.

Lors du démarrage de l’application, l’évènement « Startup » est déclanché, si on regarde le comportement de l’application au Startup nous allons trouver la ligne de code suivante :

this.RootVisual = new MainPage();

A l’aide de cette ligne nous allons instancier notre conteneur d’écrans (pages) qui est ici MainPage.

Allons du coup du côté de MainPage.xaml : La première chose que nous allons remarquer c’est que MainPage est un UserControl, on le voit à l’aide de la balise <UserControl> au tout début du fichier xaml. Mais au fait, comment est chargé le xaml ? parce que ce qu’on a vu c’est qu’on fait un new MainPage(). Si nous regardons le constructeur dans le code behind de Mainpage.xaml nous allons nous apercevoir qu’il y a une méthode qui s’appelle InitializeComponents

En effet, dans le « Solution Explorer », positionnez vous au niveau du projet et cliquer sur l’icône « Show all files » comme suit :

image

Vous allez voir apparaître les répertoires cachés bin et obj. Dans le répertoire obj -> Debug ouvrez le fichier qui s’appelle MainPage.g.cs. Vous allez y trouver la méthode InitializeComponents que nous cherchions. Dans cette méthode nous allons charger le xaml et tous ces contrôles. Vous trouverez un fichier .g.cs pour chaque xaml que vous créez dans votre projet.

Retournons à notre xaml MainPage maintenant, nous avons dit qu’il contient une balise <UserControl> qui disait que MainPage était un UserControl, cette balise contient quelques attributs lui pêrmettant de charger les espaces de noms de librairies comme Navigation ou Control … Ensuite on trouve 2 grandes parties :

1 - Navigation frame

 

<navigation:Frame x:Name="ContentFrame" Style="{StaticResource ContentFrameStyle}" 
                              Source="/Home" Navigated="ContentFrame_Navigated" NavigationFailed="ContentFrame_NavigationFailed">
                <navigation:Frame.UriMapper>
                  <uriMapper:UriMapper>
                    <uriMapper:UriMapping Uri="" MappedUri="/Views/Home.xaml"/>
                    <uriMapper:UriMapping Uri="/{pageName}" MappedUri="/Views/{pageName}.xaml"/>
                  </uriMapper:UriMapper>
                </navigation:Frame.UriMapper>
            </navigation:Frame>

Navigation frame va acceuillir une navigation page qui sera définie par la logique d’uri mapping. En fait notre UserControl MainPage joue le rôle de conteneur comme une MasterPage en ASP.NET : nous allons mettre dedans l’allure générale que doivent avoir nos pages et nous allons définir les zones (Navigation frame) dans les quelles nous allons acceuillir des navigation pages.

L’uri mapping, permet de faire le mapping entre l’url demandée et la page délivrée au client. Regardons tout d’abord la balise <navigation:Frame> qui est caractérisée par les attributs suivants :

- x:Name : le nom du contrôle

- Style : son style (Styles.xaml)

- Source : l’Uri par défaut

- Navigated : définit ce que doit faire l’application après qu’on ai navigué vers une page

- NavigationFailed : définit ce que doit faire l’application lorsque la navigation vers une page échoue

Ici nous définissons quelques règles de mapping d’uri en disant que si la page demandée est vide (Uri= «») donc l’url demandée ressemble à celle-ci : http://mondomaine/NavigationAppTestPage.aspx la navigation frame va alors contenir la page par défaut qui est /Views/Home.xaml. Et si la page demandée est /{PageName} la navigation frame va alors contenir la page /Views/{PageName}.xaml. Essayons maintenant d’analyser notre situation de départ en démarrant le projet, nous allons lancer la page NavigationAppTestPage.aspx et le navigation frame va demander l’Uri « /Home » définie dans l’attribut « Source » de la balise « navigation :Frame ». l’Uri /Home va donc être mappée à /Views/Home.xaml grâce à cette règle :

<uriMapper:UriMapping Uri="/{pageName}" MappedUri="/Views/{pageName}.xaml"/>

et la navigation frame va donc contenir la page Home.xaml.

2- Banière

        <Grid x:Name="NavigationGrid" Style="{StaticResource NavigationGridStyle}">

            <Border x:Name="BrandingBorder" Style="{StaticResource BrandingBorderStyle}">
                <StackPanel x:Name="BrandingStackPanel" Style="{StaticResource BrandingStackPanelStyle}">

                    <ContentControl Style="{StaticResource LogoIcon}"/>
                    <TextBlock x:Name="ApplicationNameTextBlock" Style="{StaticResource ApplicationNameStyle}" 
                               Text="Application Name"/>

                </StackPanel>
            </Border>

            <Border x:Name="LinksBorder" Style="{StaticResource LinksBorderStyle}">
                <StackPanel x:Name="LinksStackPanel" Style="{StaticResource LinksStackPanelStyle}">
                    <HyperlinkButton x:Name="Link1" Style="{StaticResource LinkStyle}"
                                   NavigateUri="/Home"   TargetName="ContentFrame" Content="home"/>
                                     
                    <Rectangle x:Name="Divider1" Style="{StaticResource DividerStyle}"/>

                    <HyperlinkButton x:Name="Link2" Style="{StaticResource LinkStyle}" 
                                     NavigateUri="/About" TargetName="ContentFrame" Content="about"/>

                </StackPanel>
            </Border>
        </Grid>

Ce code permet de dessiner la banière qu’il y a en haut avec le logo, le nom de l’application et les boutons, nous n’allons pas nous attarder sur cette partie parce que c’est uniquement l’utilisation de composants graphique.

Le répertoire /Views

Concentrons nous tout d’abord sur Home.xaml et About.xaml. Ces deux fichiers sont de type navigation page. Les navigation pages est un apport de la version 3 de Silverlight. En fait, Silverlight 3 embarque un framework de navigation et du coup nous pouvons désormais naviguer entre les pages xaml d’une application Silverlight en utilisant le navigateur.

Il ne nous reste plus qu’à jeter un coup d’œil sur la page ErrorWindow.xaml qui est dans le répertoire Views. Cette page est de type childWindow, dans la terminologie web c’est une popup.

Résumé

Voila voila, l’analyse du projet exemple est finie, nous avons vu :

- Que App.xaml est le point d’entrée de l’application Silverlight.

- Que dans App.xaml nous pouvons définir les ressources de l’application.

- Dans App.xaml.cs, nous définissons notre UserControl conteneur en faisant this.RootVisual= new MainPage() dans le code behind de App.xaml.

- Dans MainPage.xaml nous définissons l’ossature de l’application (un peu comme une Master Page) et les zones (Navigation Frame) qui peuvent acceuillir du contenu(Navigation Pages).

- Que dans une Navigation frame nous pouvons définir l’uri mapping.

- Les pages Home et About sont de type Navigation Page et sont hébergés par une navigation frame.

- Silverlight 3 embarque un framework de navigation permettant de naviguer entre les pages d’une application silverlight.

- Une ChildWindow est l’équivalent d’une popup dans le monde web.

Il reste néanmoins énormément de choses à voir comme les composants graphiques, comment accéder aux données serveur (puisqu’on est côté client), Les dependency proporty et attached property, DataBinding, … nous allons essayer de couvrir tous ces points dans de prochains posts.

Tuesday, October 20, 2009 8:19:00 PM (Romance Standard Time, UTC+01:00) #    Comments [0]  | 

 

Entity framework V2, vite un successeur!!!#

A chaque fois que j’arrive sur un nouveau projet et que la question d’accès aux données se pose, les différents participants à ce projet pensent tout de suite à Entity Framework et quand on pose la question pourquoi la réponse est toujours la même : d’un côté il y a le designer et de l’autre côté ça génère les classes et hop!!! doucement doucement, on inspire, on expire, … il faut arrêter de regarder le bout de son nez, je vais vous démontrer qu’en regardant un peu plus loin le choix n’est plus aussi évident.

Tout d’abord ce qu’il faut savoir c’est que lorsqu’on fait appel à entity framework pour accéder aux données il nous renvoie des classes qui héritent de EntityObject appartenant à l'espace de nom "System.Data.Objects.DataClasses" communément appelés entités. Ces entités ont été générés par EF et sont la traduction objet du modèle de données, ici deux remarques s’imposent : La première c’est qu’ Entity Framework ne supporte pas les POCO. Et la deuxième c’est que si on prétend autant soit peu faire de la Programmation Orientée Objet, les classes générées par EF ne peuvent pas faire office de classes métiers, dans la POO on ne part pas du modèle de données pour en déduire le métier c’est plutôt le contraire.

Ensuite, si on travaille sur un vrai projet (architecture en couche …) on ne peut utiliser les classes générées par EF dans la couche métier puisque :

- On avait dit plus haut que ce n’est pas des classes métiers.

- Il ne faut pas oublier que les entités appartiennent à un contexte EF et par conséquent elles sont mappées directement sur des tables en base de données, un exemple : supposons qu’on veuille introduire du traitement métier on va commencer à manipuler nos entités EF or si on fait un new sur une entité ça crée une nouvelle ligne dans le contexte et dès qu’on va faire un AcceptAllChanges sur le contexte cette ligne vide va être créée en BDD alors qu’on voulait juste créer un objet temporaire!!! pareil pour les modifications sur des entités. Donc si on manipule les entités on manipule forcément les tables de la BDD.

- On crée une corrélation forte de toutes les couches avec l’ORM

- On crée une corrélation forte des couches entre elles, un changement au sein d’une couche signifie un changement dans toute l’application, …  bref ça remet en cause la fiabilité de l’application, sa maintenabilité, …

Ce qu’on va faire pour éviter tout ça, on va écrire les classes métiers et on va devoir transformer à l’aide de requêtes linq nos objets EF en objets métiers. Donc au final, on a fait le mapping entre les entités EF et nos classes métier.

Enfin, la généricité n’est pas le point fort d’EF. Je m’explique : en général on se lasse très vite d’écrire une classe par table surtout si c’est pour faire du CRUD (Create, Read, Update, Delete). Donc on essaye d’écrire une classe générique à la quelle on passe le type en paramètre. Malheureusement avec la version actuelle (V2) d’EF ce n’est pas possible, et là tout ceux qui l’ont déjà fait vont se lever et dire “mais si je l’ai fait”. Prenons un exemple, pour insérer dans une table Personne dans la base de données, il faut faire :

DataContext context = new DataContext();

Personne p = new Personne(“Dupond”, “Pierre”);

context.AddToPersonne(p);

context.AcceptAllChanges();

Vous l’avez bien compris le problème se pose au niveau de l’instruction context.AddToPersonne(p); elle n’est pas du tout générique. Les développeurs d’EF y ont pensé et nous mettent à disposition la méthode context.AddToObject(string entitySetName, object entity); Mais malheureusement je n’ai jamais jusqu’à présent trouvé un moyen de récupérer l’entitySetName parce que la subtilité est là!!! l’entitySetName n’est pas le nom du type de l’entité et peut être différent de ce dernier, vous trouverez la notion d’entitySet dans les fichiers SSDL, CSDL et C-S mapping. Donc écrire quelque chose comme context.AddToObject(typeof(T).Name, monObjetAInserer) n’est pas une solution. Donc au final, comme on ne peut pas écrire une classe générique permettant de faire les accès aux données CRUD pour toutes les tables, on va être obligé d’écrire une classe par table et le supposé temps qu’on a gagné en générant les classes avec EF on va le perdre dans cette phase.

Néanmoins, il existe plusieurs manières de contourner ce problème comme générer ces classes à l’aide du “Domain class Service” de .NET RIA Services ou bien utiliser tout simplement un générateur de code pour générer des classes qui font du CRUD sur toutes nos entités. Mais, à mon avis, en faisant ça on contourne le problème on ne le résout pas.

Conclusion :

Pour résumer, la génération de classes d’Entity Framework ne nous avance pas plus que ça puisque :

- Entity Framework ne supporte pas les POCO.

- On va écrire les classes métiers et on va devoir transformer à l’aide de requêtes linq nos objets EF en objets métiers

- Comme on ne peut pas écrire une classe générique permettant de faire les accès aux données CRUD pour toutes les tables, on va être obligé d’écrire une classe par table.

Pour toutes ces raisons j’ai abandonné les développements avec Entity Framework en attendant la prochaine version qui devrait répondre à toutes ces problématiques. En attendant je ne peux que vous recommander l’utilisation d’NHibernate qui est un ORM qui se base sur des fichiers XML pour décrire le mapping entre la base de données et les classes métiers, donc il y a un petit investissement en temps au début pour écrire les fichiers XML mais au final on a gain de productivité énorme puisqu’il résout toutes les problématiques énoncées ci-dessus. Pour ceux qui n’ont pas envie d’écrire les fichiers XML, vous pouvez utiliser fluent NHibernate qui vous permet de définir le mapping en code c#.

Thursday, September 17, 2009 9:24:03 AM (Romance Standard Time, UTC+01:00) #    Comments [1]  | 

 

Silverlight : introduction à l’Isolated Storage#

L’isolated storage est une fonctionnalité assez intéressante puisque c’est un espace disque isolé (comme son nom l’indique) côté client auquel on a accès à partir de notre application Silverlight, sa taille initiale est de 1Mo et peut augmenter avec son contenu. On peut alors librement créer des répertoire, des fichiers, les supprimer, …

A quoi ça peut servir?

On peut l’utiliser pour stocker des cookies, ou stocker des données que j’aurais stocké en session, ou encore stocker des fichiers propres à l’utilisateur (sa photo).

Pour utiliser l’Isolated storage, rien de plus simple :

using System.IO.IsolatedStorage;
Ensuite on a besoin d’un objet de type IsolatedStorageFile
IsolatedStorageFile isolatedStorage = IsolatedStorageFile.GetUserStoreForApplication();

Puis il ne rester plus qu’à appeler la bonne méthode pour ce que l’on veut faire.

isolatedStorage.CreateDirectory(path);

isolatedStorage.CreateFile(path);

isolatedStorage.DirectoryExists(path);

isolatedStorage.OpenFile(path, FileMode.Append, FileAccess.Write);

Voici un petit code qui permet de créer 2 répertoires et un fichier dans le premier répertoire :

isolatedStorage.CreateDirectory("rep1");
isolatedStorage.CreateDirectory("rep2");
isolatedStorage.CreateFile("rep1/fichier1");

Attention :

- vu qu’on utilise l’espace disque de la machine cliente il se peut qu’on en demande à un certain moment et qu’il n’en reste plus, ou encore que l’administrateur limite l’espace disque utilisateur.

- Il est possible moyennant quelques efforts de trouver les répertoires de l’isolated storage, donc la solution n’est pas complètement sûre, il ne faut donc pas y mettre de données sensibles.

On verra dans un prochain post comment utiliser l’Isolated Storage avec Silverlight 3 pour faire une application qui fonctionne en mode déconnecté.

a+ ;)

Monday, September 07, 2009 4:58:59 PM (Romance Standard Time, UTC+01:00) #    Comments [0]  | 

 

Silverlight 3, mon premier projet (Hello world)#

Ce post est destiné à ceux qui veulent se lancer dans des développements Silverlight et qui se posent plein de questions, voici un petit tuto du classique “Hello world” :)

 

Pour commencer il faut déjà installer les pré requis :

- Visual Studio 2008 Service Pack 1

- Silverlight™ 3 Tools for Visual Studio 2008 SP1 (Contient le runtime)

Après avoir installé tout ça, lancer Visual Studio –> Nouveau  –> projet. Choisir l’élément “Silverlight” dans l’arbre à gauche et choisi le type de projet “Silverlight Application”.

ProjectChoice

Si vous n’avez pas installé les .NET RIA Services (Ce qui n’est pas un pré requis pour ce post) vous ne devriez pas voir les 2 derniers types de projets à savoir “.NET RIA Services Class Library” et “Silverlight Business Application”

Cliquer sur OK, Le wizard vous donne alors le choix de créer en même temps que votre application Silverlight, le site web qui va héberger cette dernière. Vérifier que la case à cocher “Host the Silverlight application in a Web site” est bien active et cliquer sur OK.

AttachToWebSite 

et c’est finit, vous avez créé votre premier projet Silverlight!!! Regardons maintenant la solution Visual Studio.

Solution

Le Wizard nous a bien créé 2 projets :

- Notre application Silverlight

- L’application web qui héberge l’application Silverlight

L’application web contient 2 pages web de test qui permettent de tester notre application silverlight, le plus important à ce stade c’est de savoir que ces pages utilisent le plugin Silverlight installé sur le navigateur qui va exécuter le binaire résultant de la compilation de notre projet Silverlight qui est ClientBin/SilverlightApplication3.xap

Bien évidemment, si on lance l’application web à ce stade on tombe sur une page vide (Génial!!! :))

Si in regarde un peu plus en détail notre application Silverlight on trouve App.xaml et MainPage.xaml. Le xaml est du xml qui va nous permettre de définir notre interface utilisateur de manière déclarative au sein d’une application Silverlight ou WPF

- App.xaml permet de déclarer les ressources. Dans son code Behind, on peut réagir aux évènements au niveau de l’application, et c’est là aussi qu’on définit l’écran / User Control qu’on veut lancer au démarrage de l’application, par défaut c’est MainPage.xaml.

- MainPage.xaml : C’est le User control par défaut, on peut y rajouter des contrôles pour construire notre première IHM en Silverlight

Et c’est ce que nous allons faire … Ouvrez votre MainPage.xaml, trouvez la balise <Grid> et insérer dedans ce bout de code :

        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="110"/>
            <ColumnDefinition Width="300"/>
            <ColumnDefinition Width="110"/>
        </Grid.ColumnDefinitions>

        <Grid.RowDefinitions>
            <RowDefinition Height="60"/>
            <RowDefinition Height="60"/>
            <RowDefinition Height="60"/>
        </Grid.RowDefinitions>
        <Button Name="btnTryMe" Content="Show Hello" Grid.Row="1" Grid.Column="1" Click="Button_Click"></Button>

 

Ouvrez ensuite le code behind de MainPage.xaml et y insérer le code suivant :

        private void Button_Click(object sender, RoutedEventArgs e)
        {
            MessageBox.Show("Hello World");
        }

Et voila, notre application Hello World est finie, si vous lancez votre application maintenant, vous trouverez un bouton et si vous cliquez dessus vous devriez avoir quelque chose comme ça :

image

Ce qu’il faut toujours garder en tête c’est que lorsqu’on fait du Silverlight on fait du code qui s’exécute côté client, on n’a donc pas accès à la base de données directement, il faut faire attention au cross-domain …

Pour aller plus loin mais toujours dans le Hello World Spirit, cliquer sur Fichier –>Nouveau –> Projet. Choisir l’élément “Silverlight” dans l’arbre à gauche et choisi le type de projet “Silverlight Navigation Application”.

image

Cliquer sur OK, puis cliquer encore sur OK pour créer le projet web correspondant, le wizard va nous créer cette fois-ci une application Silverlight sample, remarquez le dossier “Views” contenant les écrans de l’application.

image

Lancer le projet, vous devriez avoir une page Home comme celle-ci :

image

l’URL est la suivante : http://localhost:52523/SilverlightApplication4TestPage.aspx#/Home

Ci on clique sur “about” on obtient l’URL suivante http://localhost:52523/SilverlightApplication4TestPage.aspx#/About

et enfin si on clique sur “Back” du navigateur on revient  à l’URL de la page Home, Génial!!! C’est une des nombreuses nouveautés de Silverlight 3, ce dernier embarque un framework de navigation et du coup on peut utiliser la navigation du navigateur même pour les applications Silverlight

Et voila, c’est fini pour ce post, on a fait un Hello World et vu une petite nouveauté Silverlight 3, on verra dans de prochains posts sur Silverlight beaucoup de nouveautés de cette version 3, mais en attendant, n’hésitez pas à laisser vos commentaires :)

Sunday, September 06, 2009 4:04:08 AM (Romance Standard Time, UTC+01:00) #    Comments [0]  | 

 

Silverlight 3: les nouveautées#

La version 3 de Silverlight ramène une longue liste de nouveautés tant au niveau ergonomique qu’accès aux données, on peut principalement en citer :

- Framework de navigation (historique du navigateur)

- Ergonomie : De nouveaux contrôles sont apparus comme le “Silverlight Child Window” ou encore “DomainDataSource”

- SLOOB : Silverlight out of browser

Et plein d’autres fonctionnalités avec notamment la sortie des .NET RIA Services qui nous permettront d’exposer la logique métier aux applications clientes de type Silverlight ou encore AJAX.

Nous ferons un tour d’horizon de toutes ces fonctionnalités dans de prochains posts.

Thursday, September 03, 2009 4:44:39 PM (Romance Standard Time, UTC+01:00) #    Comments [0]  | 

 

Injection de dépendances #05 : Injection de dépendances à l’aide du projet Castle#

Dans cet article on va utiliser Castle Windsor pour faire de l’injection de dépendances au sein de l’exemple précédemment établit dans le premier article et modifié dans le second . Pour cela on va tout d’abord télécharger et installer le projet Castle Windsor.

Comme tous les frameworks implémentant l’injection de dépendances, Castle Windsor met à disposition un container (ou Kernel) permettant de résoudre les dépendances. Il faut bien entendu signifier à ce dernier les dépendances avant de lui demander de les résoudre.

Côté implémentation c’est dans la même idée que Ninject, sauf qu’on peut déclarer les dépendances soit par code soit dans le fichier de configuration .config

On va commencer par ajouter les références aux librairies Castle nécessaires pour faire de l’injection de dépendances dans le projet console à savoir : Castle.Core.dll, Castle.MicroKernel.dll et enfin Castle.Windsor.dll.

Pour pouvoir charger ces dll, il suffit de rajouter à votre app.config les lignes suivantes :

 

<?xml version="1.0" encoding="utf-8" ?>
<
configuration>

...
        <
configSections>
                <
section name="castle" type="Castle.Windsor.Configuration.AppDomain.CastleSectionHandler, Castle.Windsor" />
        </
configSections>

...

</configuration>

 

Si vous n’avez pas de fichier .config dans votre projet, vous pouvez le rajouter en faisant click droit sur le projet -> Add -> New Item… et choisir l’élément « Application Configuration File »

Ensuite on va déclarer les dépendances au container de Castle, pour cet article j’ai choisi de la faire dans le fichier de config. Les deux types de classes qu’on va injecter pour notre exemple sont la classe RizAuCurry et la classe Poelle. Pour ce faire il faut rajouter ces quelques lignes dans le fichier de config :

 

<?xml version="1.0" encoding="utf-8" ?>
<
configuration>

...
        <
castle>
                <
components>

                        <
component id="Cuisinier"
                                           type="DemoLibrary.Implementation.Cuisinier, DemoLibrary">

                        </
component>
                        <
component id="Ustensile"
                                           service="DemoLibrary.Interfaces.IUstensile, DemoLibrary"
                                           type="DemoLibrary.Implementation.Poelle, DemoLibrary">

                        </
component>
                        <
component id="Plat"
                                           service="DemoLibrary.Interfaces.IPlat, DemoLibrary"
                                           type="DemoLibrary.Implementation.RizAuCurry, DemoLibrary">

                        </
component>

                </
components>
        </
castle>

...

</
configuration>

 

La balise Component nous permet de déclarer une dépendance. L’attribut id correspond au nom qui sera utilisé pour la résolution de dépendance, l’attribut service désigne l’interface à résoudre et est composé de 2 parties : le namespace + nom de l’interface et le nom de la dll. Enfin l’attribut type qui permet de spécifier le type de l’objet qui sera retourné par le kernel lors de la résolution de dépendance et qui est aussi composée de 2 parties : le namespace + nom de l’interface et le nom de la dll.

Maintenant il ne reste plus qu’à écrire le code principal dans notre projet console et au lieu d’instancier directement les classes on va demander au container Caste de nous renvoyer une instance du type dont on a besoin. Et ce qui est intéressant avec Castle c’est qu’on bénéficie de la résolution récursive de dépendances et qu’on n’a pas besoin de demander au container de résoudre toutes les dépendances une par une mais en faisant l’instanciation, à chaque fois qu’il va rencontrer une dépendance il va voir dans son fichier de config s’il peut la résoudre ce qui réduit considérablement le code à écrire.

    class Program
    {
       
static void Main(string[] args)
        {
           
//Instancier le container castle
            IWindsorContainer container =
           
new WindsorContainer(
               
new XmlInterpreter(new ConfigResource("castle")));

           
//Demande de r?solution des d?pendances de Cuisinier 
            //et de retourner une instance
            Cuisinier cuisinier = container.Resolve<Cuisinier>();

           
Console.WriteLine(cuisinier.Cuisiner());

           
Console.Read();
        }
    }

 

Voici le résultat :

 

clip_image002

Avantages :

- Le fait d’utiliser le fichier de config pour la déclaration de dépendances présente un avantage certain qui est de pouvoir changer le type retourné par le container sans avoir besoin de recompiler tout le code ;

- Simple à mettre en œuvre ;

- Le container est générique ;

- Résolution récursive de dépendances.

Inconvénients

- La déclaration de dépendances au sein du fichier de config peut être une source d’erreurs lors de l’exécution puisque ce n’est pas du compilé.

Conclusion :

L’implémentation de l’injection de dépendances au sein du framework Castle Windsor est très complète, souple et puissante. La prise en main est simple et le code à écrire se trouve réduit. La seule question qui reste ouverte c’est la question de la performance.

c#3 | Castle Project | DI | IOC
Friday, December 19, 2008 1:51:59 PM (Romance Standard Time, UTC+01:00) #    Comments [0]  | 

 

Injection de dépendances #04 : Injection de dépendances à l'aide de Spring.NET#

Dans cet article on va utiliser Spring.NET pour faire de l’injection de dépendances au sein de l’exemple précédemment établit dans le premier article et modifié dans le second . Pour cela on va tout d’abord télécharger et installer Spring.NET.

Le framework Spring.NET est basé sur la version java du framework Spring. Il permet de faciliter et d'accélérer le développement d'applications .NET en proposant des implémentations de différentes fonctionnalités comme la programmation orientée aspect (AOP), l'accès aux données avec NHibernate ou encore l'injection de dépendances.

Comme tous les frameworks implémentant l’injection de dépendances, Spring.NET met à disposition un container permettant de résoudre les dépendances. Il faut bien entendu signifier à ce dernier les dépendances avant de lui demander de les résoudre.

Côté implémentation c’est totalement différent des implémentations des autres frameworks. La déclaration des dépendances se fait dans le fichier de configuration .config

On va commencer par ajouter la référence à Spring.Core.dll dans le projet console.

Pour pouvoir charger cette dll, il suffit de rajouter à votre app.config les lignes suivantes :

 

<?xml version="1.0" encoding="utf-8" ?>
<
configuration>

...
        <
configSections>
                <
sectionGroup name="spring">
                        <
section name="context" type="Spring.Context.Support.ContextHandler, Spring.Core" />
                        <
section name="objects" type="Spring.Context.Support.DefaultSectionHandler, Spring.Core" />
                </
sectionGroup>
        </
configSections>

...
</
configuration>

 

Si vous n’avez pas de fichier .config dans votre projet, vous pouvez le rajouter en faisant click droit sur le projet -> Add -> New Item… et choisir l’élément « Application Configuration File »

Ensuite on va déclarer les dépendances au container de Spring. En fait en ce qui concerne Spring on définit des alias sur les types qu’on veut injecter. Les deux types de classes qu’on va injecter pour notre exemple sont la classe RizAuCurry et la classe Poelle. Pour ce faire il faut rajouter ces quelques lignes suivantes dans le fichier de config :

 

<?xml version="1.0" encoding="utf-8" ?>
<
configuration>

...


        <
spring>
                <
context>
                        <
resource uri="config://spring/objects" />
                </
context>
                <
objects xmlns="http://www.springframework.net">
                        <
object name="Plat" type="DemoLibrary.Implementation.RizAuCurry, DemoLibrary" singleton="false" />
                        <
object name="Ustensile" type="DemoLibrary.Implementation.Poelle, DemoLibrary" singleton="false" />
                </
objects>
        </
spring>

...

</
configuration>

 

La balise Object nous permet de définir un alias vers un type. L’attribut name correspond à l’alias, l’attribut type est composé de 2 parties : le namespace + nom de la classe et le nom de la dll. Enfin l’attribut singleton permet de spécifier si c’est un singleton.

Ensuite, on va créer un « wrapper » sur le container de Spring, qui va juste nous renvoyer le container lorsqu’on en a besoin. On pourrait bien évidemment se passer de cette étape mais un peu d’organisation ça ne fait pas de mal.

using Spring.Context;
using Spring.Context.Support;

    public static class Context
    {
       
private static IApplicationContext applicationContext = null;

       
static Context()
        {
            applicationContext =
ContextRegistry.GetContext();
        }

       
public static IApplicationContext ApplicationContext
        {
           
get { return applicationContext; }
        }

    }

 

Maintenant il ne reste plus qu’à écrire le code principal dans notre projet console et au lieu d’instancier directement les classes on va demander au container Spring de nous renvoyer une instance du type dont on a besoin.

    class Program
    {
       
static void Main(string[] args)
        {
           
//Le container Spring renvoye un objet qui
            //correspond ? l'alias Plat
            IPlat plat = (IPlat)Context.ApplicationContext["Plat"];

           
//Le container Spring renvoye un objet qui
            //correspond ? l'alias Ustensile
            IUstensile ustensile = (IUstensile)Context.ApplicationContext["Ustensile"];

           
//Instaciation de la classe Cuisinier
            Cuisinier chef = new Cuisinier(ustensile,plat);
            
           
Console.WriteLine(chef.Cuisiner());

           
Console.Read();
        }
    }
 

 

Voici le résultat :

image

Avantages

L’implémentation de l’injection de dépendances avec le framework Spring.NET offre beaucoup d’avantages :

- Le fait d’utiliser le fichier de config pour la déclaration des alias présente un avantage certain qui est de pouvoir changer la classe vers laquelle pointe un alias sans avoir besoin de recompiler tout le code.

Inconvénients

- Pas de résolution récursive de dépendances, on doit explicitement demander au container les objets correspondant aux alias dont on a besoin.

- Le container n’est pas générique ce qui nous oblige à faire un cast à chaque fois que ce dernier nous renvoie un objet.

- La déclaration des alias au sein du fichier de config peut être une source d’erreurs lors de l’exécution puisque ce n’est pas du compilé.

Conclusion :

Spring.NET est un bon framework d’injection de dépendances grâce à son côté souple concrétisé par l’utilisation du fichier de config mais je dois avouer que le fait de ne pas avoir la résolution récursive de dépendances est un inconvénient non négligeable et qui est à mon avis éliminatoire au sein d’un grand projet.

c#3 | DI | IOC | Spring
Thursday, December 18, 2008 11:44:47 PM (Romance Standard Time, UTC+01:00) #    Comments [2]  | 

 

Injection de dépendances #03 : Injection de dépendances en utilisant Ninject#

Dans cet article on va utiliser NInject pour faire de l’injection de dépendances au sein de l’exemple précédemment établit dans le premier article et modifié dans le second . Pour cela on va tout d’abord télécharger et installer NInject.

Ensuite on va ouvrir notre solution de l’exemple, et on va ajouter une référence dans le projet console vers la librairie Ninject.Core.dll.

clip_image002

NInject.Core est la librairie qui va nous permettre d’implémenter l’injection de dépendance.

Tout d’abord on doit commencer par déclarer les dépendances c'est-à-dire que telle interface correspond à telle implémentation, ceci se fait dans un « Module ».

Pour ce faire, on va tout simplement implémenter un module personnalisé et surcharger sa méthode load de la classe StandardModule fournie par NInject de la manière suivante :

    public class CustomModule : StandardModule
    {
       
public override void Load()
        {
           
//D?claration des d?pendances
            Bind<IPlat>().To<PattesBolognaises>();
            Bind<
IUstensile>().To<Marmitte>();
        }
    }

 

On dit dans la méthode load ci dessus que l’implémentation de l’interface IPlat est la classe PattesBlognaises, Idem pour IUstensile et Marmitte.

Pour résoudre les dépendances, il faut tout simplement instancier le container IKernel en lui passant le module qui va bien.

    class Program
    {
       
static void Main(string[] args)
        {
           
//Instanciation du module
            CustomModule module = new CustomModule();
           
//Instanciation du kernel charg? de r?soudre les d?pendances
            IKernel kernel = new StandardKernel(module);
           
//Demande de r?solution des d?pendances de Cuisinier 
            //et de retourner une instance
            Cuisinier p = kernel.Get<Cuisinier>();
           
Console.WriteLine(p.Cuisiner());

            Console.Read();
        }
    }

 

En faisant kernel.Get<Cuisinier>() on va demander au container de résoudre les dépendances de la classe Cuisinier et de nous en retourner une instance. On remarque au passage que la méthode Get est générique.

Voici ce qu’on retrouve à l’execution :

clip_image002[4]

Pour switcher d’une implémentation à une autre, on a 2 choix :

- 1er cas : On dit que c’est exclusif, c'est-à-dire qu’une interface correspond à une et une seule implémentation pour une instance de l’application, on va alors mettre des « case » dans le module pour choisir les implémentations à lier aux interfaces par rapport à un critère bien définit.

- 2ème cas : On dit que pour une instance de l’application une interface peut être liée à plusieurs implémentations. Dans ce cas, on est alors amené à implémenter plusieurs modules.

Le 1er cas étant déjà implémenté ci-dessus, je vais plutôt m’intéresser à implémenter le 2ème cas. Pour ce faire, je vais construire un nouveau module appelé RizModule qui va lier l’interface IPlat à l’implémentation RizAuCurry et l’interface IUstensile à l’implémentation Poelle comme suit :

    public class RizModule : StandardModule
    {
       
public override void Load()
        {
           
//D?claration des d?pendances
            Bind<IPlat>().To<RizAuCurry>();
            Bind<
IUstensile>().To<Poelle>();
        }
    }

 

Voici le programme main et son résultat :

    class Program
    {
       
static void Main(string[] args)
        {
           
//Instanciation du module
            CustomModule module = new CustomModule();
           
//Instanciation du kernel charg? de r?soudre les d?pendances
            IKernel kernel = new StandardKernel(module);
           
//Demande de r?solution des d?pendances de Cuisinier 
            //et de retourner une instance
            Cuisinier p = kernel.Get<Cuisinier>();
           
Console.WriteLine(p.Cuisiner());

           
//Instanciation du module
            RizModule rizModule = new RizModule();
           
//Instanciation du kernel charg? de r?soudre les d?pendances
            IKernel rizKernel = new StandardKernel(rizModule);
           
//Demande de r?solution des d?pendances de Cuisinier 
            //et de retourner une instance
            Cuisinier chefRiz = rizKernel.Get<Cuisinier>();
           
Console.WriteLine(chefRiz.Cuisiner());

           
Console.Read();
        }
    }

 

clip_image002[6]

Conclusion :

NInject est très simple à apprivoiser.

Le fait de pouvoir faire la liaison entre l’implémentation et l’interface par code à l’aide du « Bind » présente un l’avantage d’être du compilé et donc c’est moins générateur d’erreur qu’un fichier xml de configuration par exemple où on peut faire des fautes de frappe par exemple. En contre partie on ne dispose pas de la souplesse d’un fichier xml parce qu’à chaque fois qu’on aura envie de changer le Binding d’une interface à une implémentation on est obligé de recompiler le code, alors qu’avec un fichier xml il suffit de le mettre à jour.

Le container de NInject est générique, on récupère directement une instance de la classe souhaitée sans avoir besoin de faire un cast.

Le kernel résout récursivement les dépendances, c'est-à-dire qu’il résout les dépendances de l’objet mais aussi les dépendances des dépendances de l’objet … Ce qui nous facilite grandement le travail.

c#3 | DI | IOC | Ninject
Wednesday, November 26, 2008 12:34:54 PM (Romance Standard Time, UTC+01:00) #    Comments [0]  | 

 

Injection de dépendances #02 : Injection de dépendances à la main#

Cet article se base sur l'exemple énoncé dans l'article d' introduction sur l'injection de dépendances.

La première chose à faire pour pouvoir implémenter une injection de dépendance est d’enlever ces dépendances. Pour ce faire on va remplacer toutes les instanciations / occurrences des implémentations par l’utilisation des interfaces. On va alors créer des interfaces pour les classes et faire que ces dernières les implémentent. On pourra alors être complètement découplé des implémentations des autres classes puisqu’on n’utilisera que les interfaces dans le code de la classe. Enfin on va injecter les dépendances à partir de notre classe principale.

Pour notre exemple on va avoir une interface pour les plats et une interface pour les ustensiles.

    public interface IUstensile
    {
       
String Utiliser();
    }

 

    public interface IPlat
    {
       
String GetInformation();
    }

 

Les classes RizAucurry et Poelle vont implémenter respectivement les interfaces IPlat et IUstensile.

    //Implémente l'interface IPlat
    public class RizAuCurry : IPlat
    {
       
public String GetInformation()
        {
           
return ("du riz au curry");
        }
    }

 

    //Implémente l'interface IUstensile
    public class Poelle : IUstensile
    {
       
public String Utiliser()
        {
           
return ("à la Poelle");
        }
    }

 

En ce qui concerne la classe Cuisinier on ne va plus utiliser que les interfaces et on va passer en paramètre à son constructeur les dépendances le moment venu.

 

    public class Cuisinier
    {   //La classe Cuisinier n'utilise que des interfaces
        //et non les implémentations
        IPlat _plat;
       
IUstensile _ustensile;

       
public Cuisinier(IPlat plat, IUstensile ustensile)
        {
            _plat = plat;
            _ustensile = ustensile;
        }
       
public String Cuisiner()
        {
           
return "Je cuisine " + _plat.GetInformation() + " " + _ustensile.Utiliser();
        }
    }

 

La classe Main du projet console va ensuite créer tous les objets nécessaires et injecter les dépendances à l’instance de la Cuisinier comme suit :

    class Program
    {
       
static void Main(string[] args)
        {   
           
//Création des dépendances
            RizAuCurry platRiz = new RizAuCurry();
           
Poelle ustensilePoelle = new Poelle();
           
//Injectoin des dépendances
            Cuisinier chef = new Cuisinier(platRiz, ustensilePoelle);
           
Console.WriteLine(chef.Cuisiner());

           
Console.Read();
        }
    }

 

Et maintenant si on veut que le cuisinier cuisine autre chose que le Riz au curry à la poêle il suffit de construire les bonnes classes, que ses dernières implémentent les interfaces IPlat et IUstensile et c’est tout. Si on construit la classe PattesBolognaises qui implémente IPlat et la classe Marmitte qui implémente IUstensile on peut faire quelque chose comme ça :

    class Program
    {
       
static void Main(string[] args)
        {   
           
//Création des dépendances
            RizAuCurry platRiz = new RizAuCurry();
           
Poelle ustensilePoelle = new Poelle();
           
//Injectoin des dépendances
            Cuisinier ancienChef = new Cuisinier(platRiz, ustensilePoelle);
           
Console.WriteLine(ancienChef.Cuisiner());

           
PattesBolognaises platPattes = new PattesBolognaises();
           
Marmitte ustensileMarmitte = new Marmitte();
           
Cuisinier nouveauChef = new Cuisinier(platPattes, ustensileMarmitte);
           
Console.WriteLine(nouveauChef.Cuisiner());
           
Console.Read();
        }
    }

Voici le résultat :

clip_image002

 

Et voila comment on peut faire de l’injection de dépendance à la main.

- Remplacer les instanciations (dépendances) par l’utilisation des interfaces pour découpler les classes les unes des autres.

- Créer et injecter les dépendances quand on en a besoin

Il existe des frameworks qui mettent à notre disposition des Container et qui nous permettent d’implémenter l’injection de dépendance. Ces containers nous permettent de :

- Résoudre les dépendances après avoir déclaré la correspondance des interfaces avec leurs implémentations respectives.

- Sortir le code d’injection de dépendances de la logique de l’application

- Résolution récursive des dépendances. (Pour certains frameworks)

Parmis ces frameworks je vais m’intéresser particulièrement dans mes prochains articles à NInject, Castle Windsor et Spring.net.

c#3 | DI | IOC
Tuesday, November 25, 2008 4:19:57 PM (Romance Standard Time, UTC+01:00) #    Comments [0]  | 

 

L’injection de dépendances (Dependency Injection) #01 : Introduction#

On n’est jamais à l’abri d’un changement dans une application déjà finalisée voir déployée depuis un bon bout de temps. Bien qu’aujourd’hui la plupart des applications sont écrites de façon à ce que toutes ses couches soient complètement indépendantes en suivant le pattern MVC ou autre, il faut reconnaître que bien souvent les classes à l’intérieur d’une même couche sont toujours très dépendantes ce qui nous vaut un travail conséquent pour changer une règle métier par exemple.

C’est là où l’injection de dépendances intervient, il s’agit de construire des classes complètement découplées des implémentations des classes en utilisant les interfaces. Ensuite, on injecte la dépendance (on résout la dépendance) au moment ou on a besoin d’une instance de la classe, on dit tout simplement que telle interface correspond à telle implémentation. On peut injecter la dépendance par constructeur, par setter, … Le but est de créer des classes plus faciles à changer et indépendantes de l’implémentation des autres classes pour qu’on puisse changer l’implémentation correspondant aux interfaces très simplement et d’une façon dynamique.

L’injection de dépendances peut se faire à la main, mais il existe aussi des frameworks qui nous permettent de l’implémenter comme NIINJECT, Spring.NET ou encore Castle Windsor.

Prenons cet exemple :

Un cuisinier utilise un ustensile pour cuisiner un plat.

clip_image001

    public class Poelle
    {
       
public String Utiliser()
        {
           
return ("? la Poelle");
        }
    }

 

    public class RizAuCurry
    {
       
public String GetInformation()
        {
           
return ("du riz au curry");
        }
    }

 

    public class Cuisinier
    {
       
RizAuCurry plat;
       
Poelle ustensile;

       
public Cuisinier()
        {
            plat =
new RizAuCurry();
            ustensile =
new Poelle();
        }
       
public String Cuisiner()
        {
           
return "Je cuisine " + plat.GetInformation() + " " + ustensile.Utiliser();
        }
    }

 

Les deux classes RizAuCurry et Poelle se contentent de renvoyer leurs noms et la méthode Cuisiner de la classe Cuisinier ne fait qu’afficher quel plat prépare le cuisinier et à l’aide de quel ustensile.

Faisons maintenant une application console qui va juste instancier une classe Cuisinier et appeler la méthode Cuisiner.

    class Program
    {
       
static void Main(string[] args)
        {
           
Cuisinier chef = new Cuisinier();
           
Console.WriteLine(chef.Cuisiner());
           
Console.Read();

        }

    }

 

Voici le résultat :

clip_image002

Ce qu’on peut remarquer tout de suite en regardant ces classes c’est que la classe Cuisinier est directement dépendante des deux classes RizAuCurry et Poelle. Et qu’est ce qui se passe si on voulait faire faire autre chose au cuisinier ? parce que le Riz au curry c’est bon mais à la longue je pense que ça pourrais devenir un peu lourd.

c#3 | DI | IOC
Tuesday, November 25, 2008 1:04:29 PM (Romance Standard Time, UTC+01:00) #    Comments [0]  | 

 

Entity framework : premiers pas#

L'ADO.NET Entity Framework est un outil de mapping OR. Il nous permet d'avoir une transposition de notre base de données en modèle objet et de manipuler directement ce modèle au lieu de la base de données.

 

Intérêts

-         L'ADO.NET Entity Framework va nous générer automatiquement la couche d'accès aux données sans aucun effort ce qui nous permettra de nous concentrer sur les autres couches.

-         Cette couche va nous épargner toute la partie : j’ouvre une connexion à la base de données, je fais une requête sql, j’exécute la requête…

-         On a une transposition objet de notre base de données, on peut donc manipuler ces données avec du c# et surtout avec linq et tous les avantages de l’utilisation de linq (requête compilée, intellicence, …).

Générer le modèle à partir du wizard

Pour commencer il faut créer un projet de type ClassLibrary et le nommer.

 


Positionner vous sur le projet et click droit -> Add -> New Item. Dans « Visual C# Items », cliquer sur l'élément « ADO.NET Entity Data Model », renommez le en ce que vous voulez (eviter les caractères spéciaux et les tirets bas). Cliquer sur « Add ».

 

 

Cliquer sur l'élément « Generate from database »et cliquer sur « Next »

 

 

Choisir la connexion à la base de données qu'on veut utiliser, nommer la ConnectionString (toujours éviter les caractères spéciaux, ...) et cliquer sur Next.

 

Choisir les tables qu'on veut utiliser en cochant les checkbox correspondants, nommer le Model Namespace et cliquer sur finish.

 

 

Voici le résultat de la génération

Une fois qu'on a généré le modèle, il faut le préparer pour qu'on puisse l'utiliser.

Configuration du modèle

La première chose à faire c'est de compiler le projet contenant le .dbml.

 

Pour pouvoir utiliser le modèle il faut exporter les fichiers .csdl .msl .ssdl vers le projet qui voudra instancier le modèle. La façon la plus simple de les exporter de couche en couche est de les inclure dans la dll du projet contenant. Mais avant de le faire ouvrez le App.config et regardez la connectionString.

 

Pour inclure ces fichiers dans la dll il faut aller ouvrir le .dbml, click droit n'importe sur l'espace de travail -> Propiétés. Dans la propriété « Metadata Artifact Processing » séléctionner « Embed in output assembly ». Au passage vous remarquerez que la connectionString a changé de forme.

Compiler le projet. Voilà maintenant votre modèle est prêt à être utilisé.

 

Utilisation

Pour utiliser le modèle, il faut tout d'abord ajouter une référence au projet contenant le modèle et aussi à l'assembly System.Data.Entity. Ensuite, si on est dans le projet utilisateur final (Projet application web, projet de test, ...) on doit copier / coller la ConnectionString dans le App.Config ou Web.Config. Enfin écrire un using du namespace du modèle et using System.Data.Entity

 

Pour mon exemple ci-dessus :

 

using ActionnaireEntrepriseModele;

using System.Data.Entity;

 

Et c'est tout. On peut maintenant charger le modèle en écrivant :

 

Entities entities = new Entities();

 

on peut après ecrire des requête linq comme celle-ci :

 

var requete = from actionnaire in entities.Actionnaire

                        where actionnaire.id == id

                        select actionnaire;

 

Saturday, August 16, 2008 3:14:49 PM (Romance Standard Time, UTC+01:00) #    Comments [0]  | 

 

Jointure avec plusieurs conditions dans linq to entities#

Voici un petit problème auquel je me suis confronté dernièrement, je voulais tout simplement faire une requête linq contenant une jointure avec plusieurs conditions.

 

Prenons comme exemple les types d'entités suivants :

Je voudrais ecrire une requête linq qui me renvoie tous les employés de l'entreprise “NetGame”.

Pour ce faire je vais ecrire une jointure entre les 2 types d'entité “Entreprise” et “Employe”, cette jointure aura deux conditions :

-         la première est  employe.Entreprise.id doit correspondre à Entreprise.id

-         la deuxième est que entreprise.raisonSociale doit être égal à "NetGame"

 

Pour pouvoir mettre deux conditions dans une jointure, on doit créer dynamiquement 2 objets anonymes (new) chacun ayant comme propriétés les opérandes qui doivent composer les conditions séparés par une virgule, la comparaison va donc se faire sur les objets.

 

Soit “entities” est une instance de ObjectContext, voici la requête :

 

 var requete = from employe in entities.Employe

                       join entreprise in entities.Entreprise on

                                 new { idEntreprise = employe.Entreprise.id, raisonSociale = "NetGame" }

                      equals new { idEntreprise = entreprise.id, raisonSociale = entreprise.raisonSociale }

                      select employe;            

Friday, August 01, 2008 5:40:38 AM (Romance Standard Time, UTC+01:00) #    Comments [0]  | 

 

Relations many to many avec linq to entities#

Jai 2 tables en base de données avec une relation n-n, c'est-à-dire qu’elles sont liées avec une troisième table dont la clé primaire est composée des deux clés primaires des 2 tables.

Je génère le modèle .dbml à partir de ces trois tables pour avoir 2 types d'entités ayant chacune comme propriété une EntityCollection<Type> ou Type est l’autre type d'entité. 

Mon but est d’écrire une requête linq permettant de me renvoyer toutes les entités de type x correspondantes à une entité donnée de type y.

Prenons un exemple :

Une Entreprise peut avoir plusieurs Actionnaires et un Actionnaire peut investir dans plusieurs Entreprises.

Voici les types d'entités résultat :



Supposons que ma requête doit me renvoyer tous les actionnaires d'une entreprise 

En sachant que Contains ne marche pas pour linq to entities (domage), voici les deux manières de faire que j'ai trouvé :

1- Utiliser Any<> : on peut l'utiliser pour mettre des conditions sur chaque élément d'une collection(on aura pour résultat tous ceux qui ont satisfait la ou les conditions)

var requete = from actionnaire in entities.Actionnaire

where actionnaire.Entreprise.Any<Entreprise>(e => e.id == idEntreprise)

select actionnaire;

on peut faire par la suite une requete.ToList<Actionnaire>() pour récupérer la liste des actionnaires.

2- Ici on ne va pas se servir d'une fonction particulière sauf qu'on va passer par l'Entreprise pour récupérer ses Actionnaires. La seule chose à savoir est le type de retour de la requête.

using System.Data.Objects.DataClasses;

 IQueryable<EntityCollection<Actionnaire>> requete =

                                                                from entreprise in entities.Entreprise

                                                                where entreprise.id == idEntreprise

                                                                select entreprise.Actionnaire;

EntityCollection<Actionnaire> listeDesActionnaires =  requete.First<EntityCollection<Actionnaire>>();

En fait ici le type de retour n'est pas une  EntityCollection<Actionnaire> mais un  IQueryable<EntityCollection<Actionnaire>> parce que tout simplement il ne sait pas si notre condition dans le where va ramener 1 ou plusieurs Entreprises donc il prévoit d'avoir plusieurs  EntityCollection<Actionnaire> d'ou le IQueryable<EntityCollection<Actionnaire>>.

Donc pour récupérer la liste des actionnaires on doit faire :

requete.First<EntityCollection<Actionnaire>>().ToList<Actionnaire>();

Tuesday, July 29, 2008 8:46:25 PM (Romance Standard Time, UTC+01:00) #    Comments [0]  | 

 

Récupération d'objets dépendants par linq#

Dans le cadre d'une application n-tiers, on a tous eu besoin de transformer une liste d'éléments d'un certain type vers une liste d'éléments d'un autre type. Ce qu'on faisait avant c'est qu'on faisait un petit foreach() on parcourant la liste de départ, dans la boucle on crée un élément du type de destination on rempli ses propriétés et par la suite on ajoute l'élément à la liste de destination.

Avec linq, on peut éviter cette boucle d'une façon très élégante.

Voici un petit exemple :

supposons que l'on ait les 2 classes suivantes :

public class Personne

{

    public Int32 Id { get; set;}

    public String Nom { get; set;}

    public String Prenom { get; set;}

    public Adresse Domicile { get; set;}

}

public class Adresse

{

    public Int32 Id { get; set;}

    public String Numero { get; set;}

    public String Voie { get; set;}

    public String CodePostal { get; set;}

    public String Ville { get; set;}

    public String Pays { get; set;}

}

Dans ma couche d'accès aux données, j'ai une fonction qui me renvoie List<Personne>, et mon objectif est de renvoyer à la couche de présentation qui contient mes page web une liste d'éléments  contenant toutes les propriétés de la classe Personne et une concaténation des propriétés de son adresse. Le but est biensur de binder le résultat directement dans un gridview et ne pas avoir à traiter les données dans le fichier .aspx.cs pour avoir le rendu voulu.

On créé la classe «PersonneVille »  ayant exactement la structure qu'on veut renvoyer

public class PersonneVille

{

    public Int32 IdPerson { get; set;}

    public String Nom { get; set;}

    public String Prenom { get; set;}

    public String Domicile { get; set;}

}

Par la suite on fait ce petit code :

//personnes est l'objet de départ de type List<Personne>

var requete = from p in personnes

    select new PersonneVille

    {

      IdPerson = p.Id,

      Nom = p.Nom,

      Prenom = p.Prenom,

      Domicile = p.Domicile.Numero.ToString() + ", " +

      p.Domicile.Voie + " " +

      p.Domicile.CodePostal + " " +

      p.Domicile.Ville + " " +

      p.Domicile.Pays

    };

return requete.ToList<PersonneVille>();

Et voilà, on a remplacé notre foreach par une petite requête linq :)

c#3 | linq
Monday, July 28, 2008 6:50:18 PM (Romance Standard Time, UTC+01:00) #    Comments [1]  | 

 

All content © 2012, Zied Nemili
On this page
This site
Calendar
<February 2012>
SunMonTueWedThuFriSat
2930311234
567891011
12131415161718
19202122232425
26272829123
45678910
Archives
Sitemap
Blogroll OPML
 #Rui