1. Traduction

Cet article est la traduction la plus fidèle possible de l'article original de Brad Abrams,  Business Apps Example for Silverlight 3 RTM and .NET RIA Services July Update: Part 21: Hierarchical Data.

2. Article

Wow - c'est une série qui ne s'arrête pas... Je m'amuse *toujours* à mettre à jour ma démo d'application métier du Mix 09. Est-ce que quelqu'un est intéressé par une séance de 10 heures au PDC2009 pour passer tout ce qu'on a vu en détail ;-) ?

Quoi qu'il en soit, dans cette section, je tiens à relever un défi envoyé par Ben Hayat, qui était de montrer que Silverlight et RIA Services sont capables de construire de vraies applications métier.

La tentation de répondre était trop grande pour laisser ce sujet de coté.

Apparemment, les applications métier contiennent plus d'une table, et parfois des relations plus compliquées que maître/détails.

Mais elles comprennent également des données hiérarchiques. Par exemple, vous avez une table Orders qui est associée à un ensemble d'articles dans une table distincte LineItems. Cette table Orders assure également le suivi des ventes totales, le montant net, les taxes et le nombre de lignes, au fur et à mesure de l'ajout ou de la suppression d'articles.

Vous pouvez voir la série complète ici. Cette démo nécessite les éléments suivants (tout est 100% gratuit) :

Vous pouvez, de plus, télécharger les fichiers de la démo complète.

Pour intégrer ceci dans mon service de placement de Super héros, nous allons examiner le problème métier que représente le suivi des citations pleines d'esprit que font souvent nos super héros.

Et plus loin, nous utiliserons une technique de notation basée sur une intelligence artificielle avancée, de façon à évaluer chaque citation, et à permettre à SuperEmployee de conserver un compte de points. 

Pour ce faire, nous allons ajouter une table Quotes  

Image non disponible

Puis nous allons ajouter les relations nécessaires

Image non disponible

Puis mettre en place la relation entre la clé primaire et la clé étrangère. Un SuperEmployee peut avoir de nombreuses citations. 

Image non disponible

Enfin, nous allons rafraîchir le modèle Entity Framework de la même façon que précédemment, ce qui rend tout ceci plus visuel...

Image non disponible

Maintenant, nous devons mettre en place l'entité Quotes que nous devons renvoyer au client. Premièrement, nous devons dire à Entity Framework d'inclure Quotes dans la requête envoyée à la base de données pour récupérer les SuperEmployee...

 
Sélectionnez
public IQueryable<SuperEmployee> GetSuperEmployees()
{    
   return this.Context.SuperEmployeeSet
              .Include("Quotes")
              .Where(emp=> emp.Issues > 10)
              .OrderBy(emp=> emp.EmployeeID);
} 

Maintenant, nous devons nous assurer que la table soit envoyée au client. Nous allons donc, dans le fichier SuperEmployeeDomainService.metadata.cs, ajouter la propriété Quotes et la marquer comme étant incluse. Cela nous permet de garantir que nous enverrons effectivement les informations minimales sur le réseau. 

 
Sélectionnez
internal sealed class SuperEmployeeMetadata
{ 
   // Metadata classes are not meant to be instantiated.
   private SuperEmployeeMetadata()
   {
   }

    [ReadOnly(true)]
   public int EmployeeID;

   [Include]
   public Quotes Quotes;

Maintenant, nous allons ajouter une méthode Insert pour les Quotes. Vous noterez que nous n'avons pas besoin de Query, ou de Add, car nous ne supporterons pas ces opérations. Si vous vouliez mettre à jour l'entité serveur SuperEmployee vous pourriez le faire ici aussi... 

 
Sélectionnez
public void InsertQuote(Quotes quote)
{
   this.Context.AddToQuotes(quote);
}

Maintenant, sur le client, je voulais une interface très simple, j'ai donc remplacé le DataForm par une ListBox, de façon à lister toutes les citations déjà attribuées à ce SuperEmployee, puis une zone de texte très simple pour ajouter une nouvelle citation. 

 
Sélectionnez
<StackPanel Margin="35,30,0,0" HorizontalAlignment="Left" VerticalAlignment="Top" Height="498" >
   <ListBox x:Name="quotesList">
       <ListBox.ItemTemplate>
           <DataTemplate>
               <StackPanel Orientation="Horizontal" >
                  <TextBlock Text="{Binding Quote}"></TextBlock>
                   <TextBlock Text=" - "></TextBlock>
                   <TextBlock Text="{Binding Points}"></TextBlock>
               </StackPanel>
           </DataTemplate>
       </ListBox.ItemTemplate>
   </ListBox>
   <TextBlock Text="Quote:"></TextBlock>
   <TextBox x:Name="newQuoteTextBox" Width="400" Height="25" TextWrapping="NoWrap" />
   <Button Content="Ok" Click="Button_Click"> </Button>
</StackPanel>


Dans les lignes 4 à 8, on met en place un DataTemplate super simple pour contrôler la manière dont les guillemets sont affichés.  Puis, dans les lignes 14 à 16, un formulaire très simple pour ajouter une nouvelle citation.  Maintenant, pour écrire les données dans le formulaire. J'aurais pu faire cela avec le DataBinding, mais parfois, le faire depuis le code est tout aussi facile.

 
Sélectionnez
private void dataGrid1_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
   var emp = dataGrid1.SelectedItem as SuperEmployee;
   quotesList.ItemsSource = emp.Quotes;
}

Enfin, j'ai besoin de gérer l'événement clic sur le bouton.  

 
Sélectionnez
Random random = new Random(DateTime.Now.Second);
private void Button_Click(object sender, RoutedEventArgs e)
{
   var emp = dataGrid1.SelectedItem as SuperEmployee;
   var q = new Quotes();
   q.Quote = newQuoteTextBox.Text;
   q.SuperEmployee = emp;

   //todo: créer un vrai générateur de points... 
   q.Points = random.Next(0,10);
   emp.Quotes.Add(q);
   newQuoteTextBox.Text = "";
}

Notez qu'à la ligne 10, nous utilisons une intelligence artificielle très évoluée pour déterminer la qualité de chaque proposition… On m'a dit qu'American Idol est en train de passer à ce modèle maintenant que Paula Abdul est partie ;-)

Tous ce que dont les utilisateurs ont besoin de faire maintenant est de cliquer sur «  Submit Changes », et la méthode Insert que nous avons écrite ci-dessus sera appelée pour chaque élément ajouté. 

Image non disponible

Maintenant, nous aimerions voir le total des points pour chaque employé... Dans ce scénario, je n'ai besoin que des données nécessaires pour afficher l'interface utilisateur, je vais donc utiliser une propriété calculée sur le client.

Nous allons le faire grâce à la classe partielle SuperEmployee, où nous définissons la propriété TotalCount d'une façon évidente (lignes 12 à 18). Mais nous avons aussi besoin de remonter une notification de changement de propriété à chaque ajout d'une nouvelle citation. Nous allons donc nous inscrire à la notification de changement de propriété lorsqu'un Employee est chargé et lever cet événement (lignes 3 à 10).  

 
Sélectionnez
public partial class SuperEmployee 
{
   protected override void OnLoaded(bool isInitialLoad)
   {
       base.OnLoaded(isInitialLoad);
       this.Quotes.EntityAdded += (s, e) =>
           {
               this.RaisePropertyChanged("TotalPoints");
           };
   }

   public int? TotalPoints
   {
       get
       {
           return this.Quotes.Sum(q => q.Points);
        }
    }
}

Puis, nous ajoutons un peu de XAML dans la DataGrid et nous obtenons notre propriété calculée :

 
Sélectionnez
<data:DataGrid.Columns>
  <data:DataGridTextColumn Header="Name" Binding="{Binding Name}" />
  <data:DataGridTextColumn Header="Employee ID"  Binding="{Binding EmployeeID}" />
  <data:DataGridTextColumn Header="Origin"  Binding="{Binding Origin}" />
  <data:DataGridTextColumn Header="Total Points"  Binding="{Binding TotalPoints}" />   
</data:DataGrid.Columns>

Et l'interface utilisateur est jolie et mise à jour automatiquement. 

Image non disponible

Enfin, nous allons faire un peu plus de validation de données...  Tout d'abord, sur le serveur, dans le fichier SuperEmployeeDomainService.metadata.cs nous définissons une nouvelle classe qui nous permet d'accrocher des métadonnées supplémentaires...

 
Sélectionnez
[MetadataTypeAttribute(typeof(Quotes.QuotesMetadata))]
public partial class Quotes
{
   internal sealed class QuotesMetadata
   {
       private QuotesMetadata() { }

       [StringLength(140,
           ErrorMessage="Quote Text must be twitter length (less than 140 characters)")] 
       [Required]
       public string Quote;
   }
}

Puis, sur le client, nous ajoutons un petit peu d'interface utilisateur pour afficher l'erreur ainsi que le code pour définir le texte d'erreur... 

 
Sélectionnez
<TextBlock X:Name="errorBox" Foreground="Red"> </ TextBlock>
 
Sélectionnez
try
{
   var q = new Quotes();
   q.Quote = newQuoteTextBox.Text;
   var emp = dataGrid1.SelectedItem as SuperEmployee;
   q.SuperEmployee = emp;

   //todo: do a real point generator.. 
   q.Points = random.Next(0, 10);
   emp.Quotes.Add(q);
   newQuoteTextBox.Text = "";
}
catch (Exception validationException)
{
   errorBox.Text = validationException.Message;
}
Image non disponible

Génial... Donc dans cette partie, nous avons examiné la façon de traiter des données hiérarchiques, ce qui est très courant dans des scénarios Commandes/Détail des commandes. Nous avons également montré comment fonctionnent des propriétés calculées. J'espère que vous apprécierez !

3. Conclusion

Cet article conclut la partie sur les données hiérarchiques. La vingt-deuxième partie de cette série d'articles sera consacrée à la gestion de fichier de solutions séparés.

4. Remerciements

Je tiens ici à remercier Brad Abrams pour nous avoir autorisé à traduire son article.
Je remercie également _Max_ pour sa relecture et ses propositions.