mercredi 14 octobre 2009

Exemple d'application d'entreprises pour Silverlight 3 et .NET RIA Services. - Partie 2: L'accès aux données

Partie 2 : Accès aux données

Cet exercice va nous entrainer à l'accès aux données avec Silverlight.

Cet exercice requière (tout est gratuit et le restera) :

1- VS2008 SP1 (qui comprend Sql Express 2008)
2 - Silverlight 3.0
3 - .NET RIA Services July’09 Preview (pour cet exercice, ceci est optionnel)

L'original en anglais est diponible ici: Part 2: Rich Data Query
L'application en ligne est disponible ici: http://www.hanselman.com/abrams/#/Home

Vous pouvez télécharger la solution complète ici: MonApplication.zip

Tout d’abord, récupérons la base NORTHWND.MDF disponible ici: NORTHWND.MDF
Plaçons-là dans le répertoire MonApplication.web/AppData.



Effectuons un clic-droit sur MonApplication.web et ajoutons un nouvel item.





Allons dans Data et sélectionnons ADO.NET Entity Data Model et nommons-le Northwind.edmx.




Une nouvelle fenêtre apparait nous demandant de choisir quel type de modèle nous désirons. Sélectionnons Generate from database.





Dans la fenêtre d’option, dans Data Source, sélectionnons Change et choisissons Microsoft SQL Server Database File.






Cliquons sur le boutonBrowse de Database File Name et sélectionnons la base NORTHWND.MDF.







Nous pouvons cliquer sur le bouton Test Connection pour nous assurer que tout se passe bien.




Cliquons sur OK, et une nouvelle fenêtre apparait. Cliquons sur Next.




Sur la fenêtre suivante, nous allons sélectionner la table qui nous intéresse, en l’occurrence, ici, il faut cocher SuperEmployees (dbo). Validons notre choix en cliquant sur Finish.




A ce stade, il est alors nécessaire de compiler à nouveau la solution afin de tout prendre en compte. Pour ce faire, faisons un clic droit sur notre solution et sélectionnons Build Solution. Voilà, nous avons créé notre couche d’accès aux données.







Maintenant, une fois que tout est en place, nous allons pouvoir parler de l’accès aux données. Quasiment toutes les applications professionnelles ont besoin d’un accès aux données. Nous allons d’abord démarrer avec le projet web. Pour cet exemple, j’ai utilisé un modèle de donnée Entity Framework, mais RIA Services fonctionne très bien avec tout type de données issues d’objets ou de fichier XML, de services web au Linq to Sql.

Maintenant, la question qui se pose est : comment allons-nous accéder à ces données depuis notre client Silverlight ? Traditionnellement, la plupart des applications d’entreprises démarrent avec un modèle d’application 2-tiers. Ceci est source de nombreux problèmes en termes de flexibilité et d’évolutivité… De plus, cela ne fonctionne tout simplement pas avec l’architecture client Silverlight / web.




Du coup, les développeurs s’orientent plutôt dans le monde du n-tiers. Il est très facile, avec .NET RIA Services de créer des services n-tiers qui soient flexibles et évolutifs bâtis sur WCF et ADO.NET Data Services.




Ces services .NET RIA modélisent la logique de notre application UI-tiers et encapsulent l’accès à nos données diverses tel que les données POCO (Plain Old CLR Object), les services Cloud comme Azure, S3, etc. via REST, etc. Une des caractéristique formidable de cela, c’est que l’on peut migrer d’une base de donnée SQL Server à un service Azure distant sans rien n’avoir à changer quoique ce soit dans la logique d’application.

Voyons maintenant à quel point il est aisé de créer ces services RIA.

Effectuons un clic droit sur le projet serveur (MonApplication.web) et sélectionnons ajouter une nouvelle classe Domain Service. Nommons la nouvelle classe SuperEmployeeDomainService.cs.







Dans l’assistant, sélectionnons notre source de données (ici NORTHWNDEntities). Notez que nous pourrions choisir une classe Linq2Sql, une classe POCO, etc. Veillons également à cocher les cases Enable Client Access, SuperEmployees, Enable editing et Generate associated classes for metada.




Dans la classe nouvellement créée, nous avons une ébauche de toutes les méthodes pour accéder à nos données. Nous devrions bien sûr les modifier pour notre application. Pour les prochaines étapes, nous allons utiliser la méthode GetSuperEmployees(), donc, nous allons devoir la modifier comme suit :









Maintenant, basculons du coté du client. Tout d’abord, compilons à nouveau la solution afin d’y accéder coté client directement. Ces projets sont liés.

Effectuons un glisser-déposer de la Datagrid de la boite à outil dans notre vue Home.xaml, juste après les TextBlock, avant la fermeture du StackPanel.




Modifions alors le xaml ainsi ajouté comme suit :






Maintenant, dans le code behind (c-a-d, dans le fichier Home.xaml.cs), ajoutons la clause

using MonApplication.Web.

Notez qu’il est intéréssant que MonApplication.Web est définie sur le serveur… Nous pouvons maintenant accéder au proxy client du serveur DomainService localement.

























A la ligne 1, nous avons créé notre SuperEmployeeDomainContext… Il s’agit du SuperEmployeeDomainService coté client. Notez la convention de d’appellation ici.

A la ligne 2, nous lions les données à la datagrid que nous avons créé précédemment. Enfin, à la ligne 3, nous chargeons les données grâce à la méthode GetSuperEmployees() que nous avons défini sur le serveur.
Notez que tout cela se fait de façon asynchrone et que nous n’avons pas a nous préoccuper de la complexité du monde de l’asynchronisation.




Et voilà le résultat ! Nous avons toutes nos entrées, mais dans le vrai monde, ne voulons-nous pas pouvoir paginer et faire en sorte que le serveur trie et filtre les résultats ? Voyons-voir comment procéder.

Tout d’abord, effaçons totalement les lignes de code que nous avons ajouté en code behind (c-a-d, les trois lignes dans notre fichier Home.xaml.cs). Puis, dans notre vue Home.xaml, ajoutons un DomainDataSource en faisant un glisser-déplacer de la boite à outil.




Puis, éditons le code nouvellement inséré. Ajoutons en premier lieu l’espace de nom de notre application.



















En 1, nous avons jouté l’espace de nom.

En 2, nous appelons la méthode GetSuperEmployeesQuery du DomianContext spécifié en 4.

En 3, nous réglons la taille du chargement à 20. Ceci signifie que nous allons télécharger les données par groupe de 20.

Maintenant, nous allons lier tout cela au Datagrid et afficher un petit indicateur de chargement.

Pour ce faire, nous allons tout d’abord ajouter un espace de nom afin d’accéder à l’indicateur de progression.




Puis nous allons modifier notre vue comme suit :

NB : lorsque l'on fait un glisser-déposer d’un contrôle sur la vue, l’espace de nom est automatiquement ajouté. Ajoutons donc le DataPager de cette manière.



A la ligne 27, nous voyons le Datagrid qui est lié à la propriété DDS.Data. Puis, nous voyons un DataPager à la ligne 31, qui est lié à la même source de données. Ceci nous prodigue l’interface de pagination.
Notez qu’à la ligne 31 nous choisissons d’afficher dix enregistrements à la fois. Finalement, nous englobons l’ensemble dans un contrôle Activity afin d’afficher la progression.

Ce qui est bien avec les contrôles Activity, le Datagrid et le DataPager, c’est que l’on peut les utiliser avec n’importe quelle source de données comme les services WCF, les services REST, etc.

Appuyons sur F5 et voyons le résultat…




Notez que nous chargeons 20 enregistrements à la fois, mais que nous n’en n’affichons que 10. Ainsi, si nous avançons d’une seule page, cela sera géré uniquement par le client, mais si nous avançons d’avantage, nous faisons un appel serveur et nous téléchargeons 20 enregistrements de plus. Notez également que le tri fonctionne également. Et où se trouve le code qui gère le tri ? Avons-nous écrit le code coté client ou serveur ? Pas du tout, ceci n’est que la magie de Linq, tout cela se fait tout seul et le résultat tombe.

Nous pouvons déjà ajouter une fonction de groupement. Pou cela, nous allons tout d’abord ajouter un espace de nom comme suit :




Puis, nous joutons le code suivant :




Et voilà le résultat :




Maintenant, ajoutons un filtrage. Tout d’abord, ajoutons un Label et un TextBox.




Enfin, ajoutons ces filtres à notre DomainDataSource :




Lorsque nous appuyons sur F5, nous obtenons une boite de filtre et lorsque l’on tape quelque chose dedans, nous faisons un filtrage des résultats coté serveur.




Maintenant, supposons que nous voulons créer un champ de saisie automatique plutôt qu’une simple boite de texte. La première chose que nous devons faire est d’obtenir toutes les origines possibles. Notez que nous devons les obtenir à partir du serveur (le client risque en effet de ne pas toutes les avoir dans la mesure où nous chargeons les données par groupe de 20, que nous effectuons une pagination, etc.). Pour faire cela, nous allons ajouter une méthode dans notre DomainService. Dans le fichier SuperEmployeeDomainService.cs, ajoutons tout d’abord cette classe :





Puis la méthode dans la classe SuperEmployeeDomainService qui retourne les origines :




Dans notre vue Home.xaml, remplaçons le TextBox par un AutoCompleteBox comme ceci :




Puis ajoutons le code behind pour charger tout ça :




Lançons le tout avec F5 et nous devrions obtenir cela :




Valider la mise à jour des données

Maintenant, nous avons certes une application qui nous permet d’afficher les données de manière sophistiqué, mais les applications professionnelles nécessitent également d’autoriser la mise à jour de données. Voyons comment procéder. Tout d’abord, remplaçons le xaml ci-dessous, sous le DomainDataService. Ceci nous fournira une belle vue Master-detail.

(Veuillez vous rendre ici pour avoir le texte complet de Home.xaml).
Maintenant, lançons l’application avec F5 et nous obtenons alors ceci :




Si l’on tente de modifier un enregistrement dans la vue détail, nous voyons un astérisque nous signalant que cette entrée est modifiée et quelle devra être renvoyée au serveur. Si vous éditez une ou plusieurs entrées et que vous annulez ces modifications, l’astérisque disparaitra.




Maintenant, nous devons brancher le bouton « Envoyer ». Pour ce faire, dans votre fichier Home.xaml.cs, ajoutons le délégué suivant :




Tout d’abord, nous devons valider l’élément qui est en cours d’édition, puis nous devons juste envoyer les changements. Ces changements sont regroupés par lot et renvoyés au serveur. Puis, notre méthode Update est appelée. Notez que l’astérisque disparait alors.

Voilà qui est fait, mais qu’en est-il de la validation des données ? De base, nous avons une validation de niveau type (si c’est un entier, une chaine de caractère, etc.). Par exemple, si nous remplissons le champ EmployeeID avec une chaine de caractère, nous obtenons une erreur (NB : pour que cela fonctionne, vous devez lancer votre projet sans débogage, en appuyant sur CTRL-F5).




Maintenant, voyons comment aller un peu plus loin. Pour ce faire, nous éditons SuperEmployeeDomainService.metadata.cs sur le serveur. Il est important de faire cela sur le serveur, ainsi le système effectue toutes les vérifications dans un premier temps pour fournir une bonne expérience utilisateur et une seconde fois sur le serveur pour vérifier l’intégrité des données. Lorsque votre méthode Update est appelée sur votre DomainService, vous pouvez être sûr que la validation a été faite.

Voici quelles validations nous pouvons faire :




Maintenant, il faut reconstruire la solution pour que ces modifications soient prises en compte. Lançons l’application avec CTRL-F5 et nous voyons nos règles de validation qui fonctionnent.



Notez que nous pouvons naviguer entre les erreurs et que le focus se fait sur l’erreur sélectionnée.

Voilà pour ce qui est de la validation des données. Mais, qu’en est-il sur l’ajout de données ?

Pour ce faire, nous allons utiliser la nouvelle fenêtre enfant (ChildWindow) de Silverlight 3.

Faites un clic-droit sur View, Add… , New Item. Sélectionnez Silverlight Child Window et nommez-la AddNewWindow.




Lions cette fenêtre à un bouton afin de permettre son affichage. Tout d’abord, ajoutons un bouton au formulaire principal (Home.xaml) pour afficher cette nouvelle fenêtre :




Puis, lions-le en code behind (Home.xaml.cs):

Tout d’abord, ajoutons cette clause Using :






Puis écrivons notre évènement comme suit :








Et voilà le résultat :




Nous voyons que cette nouvelle fenêtre est déjà dotée d’un bouton OK et d’un bouton Cancel qui sont déjà fonctionnels (bien qu’ils ne fasse pas grand-chose). Nous n’avons qu’à ajouter un DataForm. Pour cela, nous n’avons qu’a reprendre le même que nous avons défini pour la mise à jour des données dans notre vue master/detail. (Vous pouvez téléchargez ce fichier ici.)




Maintenant, en code behnind, nous devons procéder à ces différentes étapes. Tout d’abord, n’omettons pas d’ajouter notre caluse Using.


Ajoutons cette déclaration de classe :





Initialisons le constructeur…





Et gérons le bouton OK :



Voilà le résultat :




Bien, maintenant, nous allons répercuter ce changement localement (Home.xaml.cs):

Tout d’abord, nous allons abonner l’évènement AddNewButton_Click à un évènement qui détectera la fermeture de la fenêtre AddNewWindow :



Et nous allons créer ce délégué :





Et voilà ! Notre application est totalement fonctionnelle. Notez que pour que les changements soient répercutés sur la base de donnée distante, une fois la fenêtre AddNewWindow fermée, nous devons cliquer sur le bouton envoyer.

6 commentaires:

  1. Merci bien c'est tres intéressant ce que vous aviez mentionné

    RépondreSupprimer
  2. Pourquoi le datagroup:FilterDescriptor ne fonctionne pas ! Il me génére une exception !!?

    RépondreSupprimer
  3. Voila l'exception qu'il me génère :

    System.Windows.Markup.XamlParseException occurred
    Message="AG_E_PARSER_BAD_PROPERTY_VALUE [Line: 47 Position: 36]"
    LineNumber=47
    LinePosition=36
    StackTrace:
    à System.Windows.Application.LoadComponent(Object component, Uri resourceLocator)
    à GestionClientsRIA.Home.InitializeComponent()
    à GestionClientsRIA.Home..ctor()
    InnerException:

    Et voila mon code :

    RépondreSupprimer
  4. Oui c'est parce que c'est impossible d'afficher le code dans les commentaires !
    Voila j'ai posté mon problème dans le forum de developpez :
    http://www.developpez.net/forums/d916679/dotnet/developpement-web/silverlight/probleme-dds-filterdescriptor/#post5180581

    RépondreSupprimer