Techdays Be 2012 - Azure, ASP.NET MVC4, Single Page Applications et le futur du C#

15. February 2012 01:02 by Renaud in   //  Tags:   //   Comments (0)

First of all, je dois remercier RealDolmen, qui m'a permis de participer aux Techdays durant mes heures de boulot. Pour ceux qui viennent d'une autre planète, les Techdays c'est L'Événement auquel il faut assister si vous êtes développeur (et que vous n'êtes pas allergique au monde Microsoft). Contrairement à ce qu'on pourrait croire, on n'y parle pas uniquement de technologies MS, mais également d'HTML5/CSS, javascript, et bonnes pratiques en général. Et puis, c'est l'occasion de rencontrer les experts locaux, et internationaux!

Cette année, c'est dans le sud du pays que ça se passe, au Kinepolis Imagibraine plus exactement. Hormis le fait qu'on est un peu à l'étroit pendant les breaks, les salles sont sympas et il y a plein de goodies à ramasser :) mais le plus important reste les sessions! On a commencé ce matin par l'opening de Katrien De Graeve, suivie de Scott Guthrie () qui nous a parlé de Windows Azure.

Représentation schématique de vos 92 instances qui tournent dans le cloud! (crédits @kRapaille)


Après ça, puisque j'avais déjà vu et revu des sessions parlant de l'interface Metro, je suis allé à la session CRM/Sharepoint. Ensuite lunch-time, et j'ai à nouveau rejoint l'audience de Scott Guthrie pour une présentation d'ASP.NET MVC4, dont la sortie est prévue pour le courant de la semaine! Excellente session... Après Scott, je suis allé écouter Bart de Smet nous parler du futur (très proche) du C#, avec notamment l'utilisation des mots clés async et await qui permettent des gérer des traitements asynchrones avec une facilité déconcertante :) Y'a plus de challenge, mais les utilisateurs apprécieront! ^^ Il nous a également parlé d'un projet sur lequel il travaille: Roslyn (comme ça, ça sonne un peu comme le prénom de votre arrière grand-mère), mais en fait c'est un projet qui consiste à proposer des APIs pour exposer le compilateur. On a eu droit à deux petites démos: une fenêtre interactive dans laquelle on peut taper du code C# et voir le résultat instantanément, on the fly. La deuxième démo montrait comment étendre les fonctionnalités de Visual Studio, comme par exemple en ajoutant un nouveau refactoring, pour réduire une full property avec un private field à une simple propriété automatique.

Ensuite, un peu d'ALM avec Brian Keller et Visual Studio 11. J'apprécie énormément l'intégration des outils de tracking avec TFS dans Visual Studio. En voyant ça, je me dis que je n'utilise même pas TFS à 10% de ses capacités :)

Et pour terminer, last but not least, Single Page Application par Steven Sanderson. Choix difficile parce que la session était en même temps que Mastering advanced concepts in Silverlight par Gill Cleeren, et Introducing Windows Runtime in Windows 8 par Bart de Smet (mais en y réfléchissant, une session level 200 pour Bart, c'est pas une vrai session de Bart ^^ ). Bref, je suis finalement allé voir de quoi parlaient les SPAs, et ça a été la claque!

Incroyable de voir avec quelle facilité on peut créer une application web avec support du mode offline, navigation côté client, et qui s'intègre à un Ipad comme une app officielle. Quelques librairies plus qu'intéressantes: UpShot.js pour gérer les requêtes asynchrones, le mode offline, le tracking des modifications sur le modèle pour le cas où l'on voudrait faire un rollback. Et KnockOut.js pour la réalisation d'une appli html5/javascript en MVVM avec binding des données, avec navigation côté client sans effectuer aucune requête. On peut mettre toutes les ressources nécessaires en cache, et au final, les seules requêtes effectuées sont celles pour communiquer avec la WebAPI REST. Le Single Page Application est un template fourni avec ASP.NET MVC4. A tester bientôt donc ;)

Bref, excellente première journée aux Techdays!

[CRM 2011] Avoid using the same name for a property and its containing type

8. February 2012 16:26 by Renaud in   //  Tags:   //   Comments (0)

CRM 2011 allows you to use the same name for a property and its containing entity. For example, you could imagine creating a custom entity like this:

Example of bad practice

However, even if it is possible, it's not a good idea because it's not allowed by the .NET Framework.

This code doesn't compile !

If you create a .NET project and try to add a new servicereference to the OrganizationData (ie: http://server/organization/XRMServices/2011/OrganizationData.sv ), an exception will be launched. The code generation will fail and you'll see the following error:

Error provided by Visual Studio 2010

By the way, if you generate the context through the crmsvcutil.exe tool, you won't face that issue. It will rename the property (ie: all_person => all_person1). It isn't possible to rename a property after it has been created, so you should consider this when starting a new Dynamics CRM 2011 project ;) It could save you some time!

[CRM 2011] Évitez de nommer une entité et l'une de ses propriétés avec le même nom!

8. February 2012 09:02 by Renaud in   //  Tags:   //   Comments (0)

CRM 2011 vous permet sans problème d'utiliser le même nom pour une entité, et l'une de ses propriétés. Par exemple, vous pourriez tout à fait créer l'entité suivante:

Exemple de mauvaise pratique

Pourtant, même si cela est possible, ce n'est pas une bonne idée parce que le Framework .Net ne l'autorise pas.

Ce code ne compile pas!

Maintenant, si vous créez un nouveau projet .Net et que vous essayez d'y ajouter une une web référence au service OrganizationData de votre organization CRM (ex: http://server/organization/XRMServices/2011/OrganizationData.sv ), une exception sera automatiquement lancée. La génération du code échouera, et vous verrez l'erreur suivante:

Erreur fournie par Visual Studio 2010

Par contre, si vous utilisez l'outil crmsvcutil.exe pour générer le contexte, par exemple pour l'utilisation du contexte fortement typé dans les plugins, alors vous n'aurez pas d'erreur. Cet outil gère correctement cette particularité et renomme automatiquement les propriétés si nécessaire (ex: all_person => all_person1). Il n'est pas possible de renommer une propriété après qu'elle ait été créée, donc pensez-y lorsque vous commencez un nouveau projet Dynamics CRM 2011!

[WP7] [MVVM] Construire une application étape par étape : 2.2/ Services d'accès aux données

6. February 2012 09:02 by Renaud in   //  Tags:   //   Comments (1)

Cet article fait partie d'une longue série (commençant ici: Introduction) dont le but est de montrer comment réaliser une application pour Windows Phone de A à Z en évitant de tomber dans les pièges les plus courants. Pour cela, je vais réaliser une application à partir de zéro et décrire les différentes étapes sur ce blog. L'application en question sera finalement publiée sur le marketplace une fois terminée. Cette application utilisera le pattern MVVM, qui va nous aider à solutionner les difficultés techniques que nous pourrions rencontrer. Elle fera appel à un webservice, mis à disposition au travers de l'API Last.FM.

Le plan:

  1. Introduction
  2. Modèle:
  3. ViewModel
    • Les vues théoriques
    • Assemblage de ViewModels
    • Actions et commandes
    • Messenger: Navigation et communication entre les ViewModels
  4. View
    • Binding
    • Templates
    • Application localisée

Services d'accès aux données:

Dans cet article, nous allons voir plusieurs choses:

  • L'intérêt d'utiliser des interfaces
  • L'utilisation des requêtes asynchrones

Les interfaces

Explorateur de solution

Une des bonnes pratiques en programmation est l'utilisation d'interfaces. Cela permet par exemple, lorsque l'on travaille à plusieurs sur un projet, de définir un contrat commun, et de travailler ensuite séparément sur les implémentations de ce contrat.

Dans notre cas, la couche d'accès aux webservices pourrait être confiée à un autre développeur, pendant qu'un premier s'occupe déjà des ViewModels faisant appels à ces services!

Mais encore mieux que ça, l'utilisation d'interfaces va vous permettre de créer plusieurs implémentations de votre couche de services! Dans l'explorateur de solution, vous pouvez voir deux projets intitulés respectivement MyShows.Services et MyShows.ServicesMock.

Le premier sera la couche réelle, celle qui sera utilisée dans notre application en production. La deuxième couche est optionnelle. Elle va me permettre de créer une fausse couche de services, qui fonctionnera sans connexion internet, et pourra par exemple servir à mettre en place des tests unitaires.

Assez parlé, regardons un peu de code!

Définition et utilisation

Premièrement, voici une petite classe qui nous servira à retourner des informations utiles:

AsyncResponse.cs
using MyShows.Common.Model;
namespace MyShows.Common.Services
{
    public class AsyncResponse
    {
        public bool HasError { get; set; }
        public string ErrorMessage { get; set; }

        /// <summary>
        /// An object implementing IEventService.
        /// </summary>
        private IEventService eventService;
    }
}

Et voici le contrat de notre service, sous la forme d'une interface:

IEventService.cs
using System;
using System.Collections.Generic;
using MyShows.Common.Model;

namespace MyShows.Common.Services
{
    public interface IEventService
    {
        void GetEventById(string id, Action<AsyncResponse, Event> callback);

        void GetEventsByPosition(float latitude, float longitude, Action<AsyncResponse, List<Event>> callback);

        void GetEventsByLocation(string location, Action<AsyncResponse, List<Event>> callback);

        void GetEventsByArtist(string artist, Action<AsyncResponse, List<Event>> callback);

        void GetEventsByCountry(string country, Action<AsyncResponse, List<Event>> callback);
    }
}

Chacune des méthodes définies dans l'interface requiert une série de paramètres, dont un callback. Si vous n'êtes pas familiers des callbacks et, voici un exemple d'utilisation d'un service implémentant l'interface ci-dessus:

        /// <summary>
        /// Calls the service.
        /// </summary>
        public void CallService()
        {
            // call eventService
            eventService.GetEventById("453454", CallServiceCallback);
        }

        /// <summary>
        /// Method called when the service has a response
        /// </summary>
        /// <param name="response">The response.</param>
        /// <param name="eventItem">The event item.</param>
        private void CallServiceCallback(AsyncResponse response, Event eventItem)
        {
            if (response.HasError)
            {
                // use response.ErrorMessage
                return;
            }

            // use eventItem
        }

Cet exemple montre la simplicité d'utilisation du service :) On fait appel à l'une des méthodes qu'il propose, en donnant en paramètre le Callback, qui correspond donc à une autre méthode de la classe appelante. Cette méthode callback doit accepter une liste d'arguments de mêmes types que ceux définis dans le service:

Action<AsyncResponse, List<Event>> callback

Un objet de type AsyncResponse, et une collection d'Event. L'AsyncResponse indique si l'appel au service s'est bien passé ou non.

Implémentation d'un service Mock

Le mock en POO est un objet qui va simuler un comportement voulu. Ici, on va créer un faux service implémentant l'interface IEventService. On pourra faire appel à ce service comme n'importe quel autre service implémentant la même interface, et sans se douter qu'il retourne de "fausses" données. Pour réaliser ces services mock, j'ai créé un nouveau projet MyShows.ServicesMock:

EventServiceMock.cs

using MyShows.Common.Services;
using System.Collections.Generic;
using MyShows.Common.Model;
using System;

namespace MyShows.ServicesMock
{
    public class EventServiceMock : IEventService
    {

        public void GetEventById(string id, Action<AsyncResponse, Event> callback)
        {
            callback(new AsyncResponse() { HasError = false, ErrorMessage = String.Empty }, EntityMocker.CreateEvent());
        }

        public void GetEventsByPosition(float latitude, float longitude, Action<AsyncResponse, List<Event>> callback)
        {
            callback(new AsyncResponse() { HasError = false, ErrorMessage = String.Empty }, EntityMocker.CreateListEvents());
        }

        public void GetEventsByLocation(string location, Action<AsyncResponse, List<Event>> callback)
        {
            callback(new AsyncResponse() { HasError = false, ErrorMessage = String.Empty }, EntityMocker.CreateListEvents());
        }

        public void GetEventsByArtist(string artist, Action<AsyncResponse, List<Event>> callback)
        {
            callback(new AsyncResponse() { HasError = false, ErrorMessage = String.Empty }, EntityMocker.CreateListEvents());
        }

        public void GetEventsByCountry(string country, Action<AsyncResponse, List<Event>> callback)
        {
            callback(new AsyncResponse() { HasError = false, ErrorMessage = String.Empty }, EntityMocker.CreateListEvents());
        }
    }
}

Chacune des méthodes de l'interface est implémentée et fait appel au callback donné en argument. Par facilité, j'ai créé une classe EntityMocker avec quelques méthodes pour créer des données d'exemple. J'ai écrit en partie cet article dans le train entre Lille et Toulon, et ce service m'a bien servi pour tester mon application en attendant d'avoir accès à internet :)

Implémentation concrète

On arrive à la partie amusante :)

Dans le code qui suit, vous pouvez voir une manière d'implémenter concrètement le service en utilisant Last.Fm et un HttpWebRequest.

D'abord, je crée une classe de base, de laquelle dériveront mes classes de services. Elle expose une propriété SynchronizationContext qui nous servira par la suite:

ServiceBase.cs
using System.Threading;

namespace MyShows.Services
{
    public class ServiceBase
    {
        protected SynchronizationContext SynchronizationContext
        {
            get { return SynchronizationContext.Current; }
        }
    }
}

Je n'ai pas implémenté toutes les méthodes ici pour ne pas surcharger l'article, alors ne vous étonnez pas si en faisant un copier/coller le code ne compile pas ! Effectivement l'interface IEventService n'est pas correctement implémentée.

Cette nouvelle classe est ajoutée au projet MyShows.Services:

EventService.cs
using System;
using System.Collections.Generic;
using System.Net;
using System.Xml.Linq;
using System.Linq;
using System.Threading;
using MyShows.Common.Services;
using MyShows.Common.Model;

namespace MyShows.Services
{
    public class EventService : ServiceBase, IEventService
    {
        private string _eventsByArtist =
                "http://ws.audioscrobbler.com/2.0/?method=artist.getevents&artist={0}&api_key={1}";

        private ApiInfo _apiInfo;

        public EventService(ApiInfo info)
        {
            _apiInfo = info;
        }

        public void GetEventsByArtist(string artist, Action<AsyncResponse, List<Event>> callback)
        {
            var requestUri = String.Format(_eventsByArtist,
                                           HttpUtility.UrlEncode(artist),
                                           _apiInfo.ApiKey);

            HttpWebRequest request = HttpWebRequest.CreateHttp(new Uri(requestUri, UriKind.Absolute));
            request.Method = "GET";

            request.BeginGetResponse(GetEventsByArtistCompleted,
                new object[] {
                    request,
                    callback,
                    SynchronizationContext });
        }

        private void GetEventsByArtistCompleted(IAsyncResult result)
        {
            WaitCallback waitCallBack =
                param =>
                {
                    AsyncResponse asyncResponse = new AsyncResponse();

                    object[] parameters = (object[])result.AsyncState;

                    HttpWebRequest request = (HttpWebRequest)parameters[0];
                    var callback = (Action<AsyncResponse, List<Event>>)parameters[1];
                    var syncrhonisationContext = (SynchronizationContext)parameters[2];

                    List<Event> events = new List<Event>();

                    try
                    {
                        HttpWebResponse response = (HttpWebResponse)request.EndGetResponse(result);

                        XDocument doc = XDocument.Load(response.GetResponseStream());

                        events = doc.Descendants("event")
                                    .Select(a => XmlMapper.LoadEvent(a))
                                    .ToList();
                    }
                    catch (Exception e)
                    {
                        asyncResponse.HasError = true;
                        asyncResponse.ErrorMessage = e.Message;
                    }
                    finally
                    {
                        syncrhonisationContext.Post(postParam=>
                            callback(asyncResponse, (List<Event>)postParam),
                            events);
                    }
                };

            //Lancement effectif du traitement
            ThreadPool.QueueUserWorkItem(waitCallBack);
        }

    // Implémenter les autres méthodes de la même manière
    }
}

Explications:

Comme expliqué plus haut, pour consommer les services de Last.Fm il suffit d'effectuer une requête GET sur une url contenant certains paramètres. Essayez par exemple d'ouvrir le lien suivant dans votre navigateur:

http://ws.audioscrobbler.com/2.0/?method=artist.getevents&artist=mgmt&api_key=b25b959554ed76058ac220b7b2e0a026&lang=fr

Ce sont ces données que l'on veut récupérer!

On commence par instancier un nouvel objet de type HttpWebRequest à partir de l'url qui nous intéresse. Ensuite on appelle la méthode BeginGetResponse à laquelle on passe un callback (appelons-le le callback interne, puisqu'il reste interne à cette couche) et un tableau d'objets. Bon, là ça devient un peu inception :) On va utiliser ce deuxième paramètre pour passer des objets qui seront alors accessibles à l'intérieur du callback interne donné en premier argument. Dans ce tableau, on place trois choses: le HttpWebRequest à partir duquel on a lancé la requête, le contexte de synchronisation dans lequel la requête a été appelée initialement pour pouvoir ensuite renvoyer les données dans le thread de départ, et pour finir le callback (externe cette fois-ci) qui avait été donné en paramètre à la méthode GetEventsByArtist.

Une fois que le webservice de Last.Fm a donné une réponse, le callback interne est automatiquement appelé et reçoit un objet IAsyncResult en paramètre.

Encore une fois, on va créer une tâche, que l'on va mettre en file d'attente pour qu'elle soit traitée dès qu'un thread est disponible. (cf. dernière ligne). Le contenu n'est pas très compliqué:

On initialise les objets nécessaires:

  • un nouvel objet de type AsyncResponse. (Rappelez-vous, c'est celui qui servira à informer celui faisant appel à cette couche de l'état de la réponse (réussie ou non).
  • Une nouvelle liste d'Event.
  • Et on récupère le contenu du tableau d'Object passé en paramètre de la méthode BeginGetResponse précédemment et qui se retrouve dans la propriété AsyncState de l'objet IAsyncResult.
On traite le résultat de la requête, en faisant appel à EndGetResponse sur la requête initiale, et en créant un nouvel objet XDocument à partir du stream dans la réponse. Et avec un peu de Linq, pour chaque noeud "event" trouvé, j'instancie un objet Event. (XmlMapper est une classe que j'ai créée pour l'occasion et qui parcourt un objet XElement pour le transformer en Event).
        public static Event LoadEvent(XElement xElement)
        {
            var newEvent = new Event()
            {
                EventId = xElement.Element("id").Value,
                Title = xElement.Element("title").Value,
                Description = xElement.Element("description").Value,
                // Etc, etc... 
            };

            return newEvent;
        }

Si le traitement ne s'est pas déroulé correctement et qu'une exception a été lancée,  on catch celle-ci, et on met à jour l'objet AsyncResponse pour indiquer qu'il y a eu une erreur. Et pour finir, dans tous les cas, on passe dans la clause finally. C'est ici qu'on va faire appel au callback externe, en prenant soin d'y faire appel dans le contexte de départ.

Si vous avez compris tout ça, vous pouvez faire de même pour le reste des méthodes et créer votre couche service très simplement. Si pas, n'hésitez pas à poser vos questions dans les commentaires :)

Pourquoi vous devriez contribuer à StackOverflow?

3. February 2012 01:02 by Renaud in   //  Tags:   //   Comments (4)

Faut-il encore vraiment présenter StackOverflow?

StackOverflow, la bible des développeurs

Toute personne ayant un jour cherché sur internet la solution à une question de développement pour  une raison diverse a forcément dû atterrir sur ce site à l'apparence douteuse! (incompréhension d'un langage oublié de tous; sombre histoire d'erreur, identifiée par un code numérique n'étant référencé dans aucune documentation; comportement estimé anormal d'un bout de code; devoir pratique à rendre pour le lendemain;  et j'en passe et des bien plus bonnes !)

Si vous êtes allergiques à l'anglais, vous allez peut-être passer directement votre chemin, mais alors shame on you! Vous n'imaginez pas ce que vous ratez.

Effectivement, StackOverflow, c'est une énorme référence! C'est le wikipedia du développeur! Ca fonctionne sur le même principe: le site, un amas de questions, est autogéré par la communauté qui répond, corrige, débat, et vote pour les meilleures questions et réponses.

Le résultat est énorme: un endroit centralisant des réponses souvent très intéressantes, détaillées et argumentées. Les propositions de réponses jugées mauvaises ou inutiles sont downvotées, et se retrouvent en bas de page histoire de ne pas gêner. Mais tout ça est régulé très sainement, puisque les droits des utilisateurs sur le site sont proportionnels à leur contribution. Ainsi, n'importe qui ne peut pas se permettre de dégrader les lieux au premier passage.

StackOverflow, à l'écoute des développeurs

Soyons honnête, il se peut que vous n'y trouviez pas la solution à tous vos problèmes, malgré ce que je viens de dire... A ce moment-là, peut-être qu'il serait temps de vous décider à poser vous-même votre propre question. C'est le premier pas de la contribution. En effet: demander, c'est déjà contribuer. N'hésitez pas alors à fournir du code, des détails, et ce que vous avez déjà tenté, si cela peut aider les autres à identifier ce qui coince!

Peut-être que votre question trouvera rapidement acquéreur, et vous pourrez alors récompenser celui qui vous aura aidé. Dans le cas contraire, gardez patience. Vous avez jeté un premier pavé dans la marre, et même si personne n'a directement de solution, vous constaterez que les autres développeurs sont souvent généreux en conseils et tenteront de la découvrir avec vous. De plus, vous serez à l'origine d'un post qui pourra peut-être permettre à des collègues faisant face au même problème de se rassembler pour apporter de nouvelles informations, ce qui aidera d'autant plus à trouver une solution. Dans tous les cas, les informations récoltées serviront plus tard à d'autres personnes, parce que vous aurez pris la peine de poser publiquement une question !

StackOverflow, valorise les développeurs

Une chose qui me plaît énormément dans la vie, et qui se dénote particulièrement bien dans le monde de la programmation, c'est que chacun peut être un expert à son propre niveau. J'entends par là qu'on a tous notre propre expérience, et qu'il est toujours possible, et agréable, d'en faire profiter quelqu'un d'autre. Il y a de nombreuses façons d'exprimer et de partager ses propres connaissances: à l'oral, en privé ou en public, lors d'une brain session, d'une formation, d'un live-meeting, de manière écrite, dans un blog, une revue, un bouquin, ... Et pourquoi ne pas faire ça comme un jeu, en contribuant à l'une des plus grandes communautés de développeurs?

J'ai mis un certain temps avant de répondre à ma première question sur StackOverflow. Au départ, j'avais toujours l'impression que les questions étaient trop compliquées pour moi. Ensuite, je suis tombé sur un sujet que je connaissais, pour avoir déjà rencontré le problème. J'y ai répondu maladroitement. Ma réponse n'a pas été acceptée par l'auteur de la question, mais un utilisateur a voté positivement pour dire qu'elle était du moins utile. Quelques jours plus tard, je remets le couvert avec deux nouvelles réponses (dont une qui parlait de Java :) j'ai encore de bons restes malgré tout! ), et là c'est la consécration! Les deux réponses sont acceptées par les auteurs, et je gagne 50 points de réputation!

C'est à moment-là que j'ai découvert l'autre côté de StackOverflow. Le côté le plus passionnant. Quand je réponds à des questions, par principe, je vérifie ce que je crois être juste avant de le poster. Et croyez-le ou pas, mais ça me fait apprendre énormément de choses ! Soit parce que je me rends compte de mes erreurs, soit parce que je comprends plus en profondeur le fonctionnement de l'un ou l'autre concept! Et pour finir, d'autres me récompensent d'un upvote. Pour moi, c'est extrêmement valorisant.

Je pense sincèrement que chaque développeur, ayant envie d'apprendre et de partager, peut prendre conscience, grâce à StackOverflow, du potentiel qu'il représente, et y trouver une source de satisfaction, et de confiance en soi.

De consommateur à acteur

Il ne tient qu'à vous maintenant, si ce n'est pas déjà fait, de changer de statut et de devenir créateur de contenu plutôt que consommateur. Vous êtes déjà un expert, à votre échelle. Faites-en profiter les autres. La création incite à la création.

Si vous êtes motivés, sachez que StackOverflow n'est pas le seul site du genre, vous pourrez en trouver d'autres susceptibles de vous intéresser sur StackExchange.

En espérant avoir suscité l'une ou l'autre vocation !

( Ps: prévenez-moi si vous dépassez un jour le score de Jon Skeet )

[WP7] [MVVM] Construire une application étape par étape : 2.1/ Le modèle client

1. February 2012 20:02 by Renaud in   //  Tags:   //   Comments (0)
Cet article fait partie d'une longue série (commençant ici: Introduction) dont le but est de montrer comment réaliser une application pour Windows Phone de A à Z en évitant de tomber dans les pièges les plus courants. Pour cela, je vais réaliser une application à partir de zéro et décrire les différentes étapes sur ce blog. L'application en question sera finalement publiée sur le marketplace une fois terminée. Cette application utilisera le pattern MVVM, qui va nous aider à solutionner les difficultés techniques que nous pourrions rencontrer. Elle fera appel à un webservice, mis à disposition au travers de l'API Last.FM.

Le plan:

  1. Introduction
  2. Modèle:
    1. Modèle client
    2. Services d'accès aux données
    3. Service de persistance des données
  3. ViewModel
    1. Les vues théoriques
    2. Assemblage de ViewModels
    3. Actions et commandes
    4. Communication entre les ViewModels (Messenger)
  4. View
    1. Binding
    2. Templates
    3. Application localisée

Modèle:

Pour commencer, on va mettre en place la couche modèle, qui contiendra le modèle client ainsi que les services d'obtention et de persistance des données.

À quoi doit ressembler le modèle?

Cela va dépendre un peu de votre scénario: Dans le cas de cette application, je vais créer un modèle proche de mon application, avec des classes implémentant déjà une interface INotifyPropertyChanged (que vous verrez plus bas), et qui est propre au framework .NET. Dans certains cas - par exemple si vous voulez stocker des données dans une base de données, ou si vous développez vous-même la partie serveur de l'application - vous pourrez tout à fait faire un premier modèle, plus basique. En effet, à quoi cela servirait de créer un webservice envoyant des données exposant l'interface INotifyPropertyChanged ou ayant des propriétés d'un type propre au framework .NET ? Cela surchargerait le modèle avec des données inutiles selon la technologie utilisée par la suite pour consommer le service.

Modèle client:

Comme nous n'avons pas accès aux sources du webservice, nous allons créer nous-même un ensemble de classes représentant les données utilisées telles qu'un événement (Event), un artiste (Artist), etc... Ces classes seront la représentation côté client des données envoyées sous forme de XML par les services des Last.FM. Ce n'est pas vraiment la partie la plus amusante, j'en conviens :) [caption id="attachment_1133" align="alignright" width="259" caption="Explorateur de solution"][/caption] Pour faire les choses proprement, j'ai créé plusieurs nouveaux projets de types Windows Phone Class Library:     Celui qui accueillera notre modèle est appelé MyShows.Common. Le modèle nécessaire pour ce projet est assez simple. On va commencer par une classe de base qui va être implémentée par l'ensemble des classes du modèle. Cette classe implémente une méthode que l'on utilisera plus tard pour notifier l'interface graphique qu'une propriété du modèle a changé. On la rencontre peut-être moins souvent, mais l'on pourrait également implémenter de la même manière l'interface INotifyPropertyChanging pour indiquer que le modèle est sur le point de changer.

ModelBase.cs
using System.ComponentModel;

namespace MyShows.Common.Model
{
    public abstract class ModelBase: INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;
        protected void RaisePropertyChanged(string propertyName)
        {
            var handler = PropertyChanged;
            if (handler != null)
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}

Ensuite, le modèle en lui-même. Voici pour exemple une partie de la classe Event, puisque c'est un peu le sujet de l'application :) Par facilité on hérite de la classe ModelBase créée juste avant. Cela évite ici simplement de devoir réimplémenter l'interface INotifyPropertyChanged dans chacune des classes du modèle.

Si vous avez déjà installé le mvvm light toolkit, pensez à utiliser le snippet propfull pour gagner du temps ;) Tapez propf + tab + tab, pour générer le code suivant: [caption id="attachment_1155" align="aligncenter" width="200" caption="propfull snippet"][/caption] En deux secondes on a une propriété complète à laquelle il ne reste plus qu'à ajouter un appel à la méthode RaisePropertyChanged implémentée dans ModelBase.
Event.cs 
using System;
using System.Collections.ObjectModel;
using System.Collections.Generic;

namespace MyShows.Common.Model
{
    public class Event : ModelBase
    {
        public Event()
        {
            Artists = new List<string>();
            Headliners = new List<string>();
        }
        private ObservableCollection<string> _artists;

        public ObservableCollection<string> Artists
        {
            get { return _artists; }
            set
            {
                _artists = value;
                RaisePropertyChanged("Artists");
            }
        }

        private ObservableCollection<string> _headliners;

        public ObservableCollection<string> Headliners
        {
            get { return _headliners; }
            set
            {
                _headliners = value;
                RaisePropertyChanged("Headliners");
            }
        }

        private string _eventId;
        public string EventId
        {
            get { return _eventId; }
            set
            {
                _eventId = value;
                RaisePropertyChanged("EventId");
            }
        }

        private string _title;
        public string Title
        {
            get { return _title; }
            set
            {
                _title = value;
                RaisePropertyChanged("Title");
            }
        }

        private string _description;

        public string Description
        {
            get { return _description; }
            set
            {
                _description = value;
                RaisePropertyChanged("Description");
            }
        }

        // Reste des propriétés....
    }
}

J'ai fait de même pour les autres classes, et je suis bon pour cette partie. Dans l'article suivant, on verra comment mettre en place une couche d'accès aux services utilisant des méthodes asynchrones.

ify;">Faut-il encore vraiment présenter StackOverflow?

StackOverflow, la bible des développeurs

Toute personne ayant un jour cherché sur internet la solution à une question de développement pour  une raison diverse a forcément dû atterrir sur ce site à l'apparence douteuse! (incompréhension d'un langage oublié de tous; sombre histoire d'erreur, identifiée par un code numérique n'étant référencé dans aucune documentation; comportement estimé anormal d'un bout de code; devoir pratique à rendre pour le lendemain;  et j'en passe et des bien plus bonnes !)

Si vous êtes allergiques à l'anglais, vous allez peut-être passer directement votre chemin, mais alors shame on you! Vous n'imaginez pas ce que vous ratez.

Effectivement, StackOverflow, c'est une énorme référence! C'est le wikipedia du développeur! Ca fonctionne sur le même principe: le site, un amas de questions, est autogéré par la communauté qui répond, corrige, débat, et vote pour les meilleures questions et réponses.

Le résultat est énorme: un endroit centralisant des réponses souvent très intéressantes, détaillées et argumentées. Les propositions de réponses jugées mauvaises ou inutiles sont downvotées, et se retrouvent en bas de page histoire de ne pas gêner. Mais tout ça est régulé très sainement, puisque les droits des utilisateurs sur le site sont proportionnels à leur contribution. Ainsi, n'importe qui ne peut pas se permettre de dégrader les lieux au premier passage.

StackOverflow, à l'écoute des développeurs

Soyons honnête, il se peut que vous n'y trouviez pas la solution à tous vos problèmes, malgré ce que je viens de dire... A ce moment-là, peut-être qu'il serait temps de vous décider à poser vous-même votre propre question. C'est le premier pas de la contribution. En effet: demander, c'est déjà contribuer. N'hésitez pas alors à fournir du code, des détails, et ce que vous avez déjà tenté, si cela peut aider les autres à identifier ce qui coince!

Peut-être que votre question trouvera rapidement acquéreur, et vous pourrez alors récompenser celui qui vous aura aidé. Dans le cas contraire, gardez patience. Vous avez jeté un premier pavé dans la marre, et même si personne n'a directement de solution, vous constaterez que les autres développeurs sont souvent généreux en conseils et tenteront de la découvrir avec vous. De plus, vous serez à l'origine d'un post qui pourra peut-être permettre à des collègues faisant face au même problème de se rassembler pour apporter de nouvelles informations, ce qui aidera d'autant plus à trouver une solution. Dans tous les cas, les informations récoltées serviront plus tard à d'autres personnes, parce que vous aurez pris la peine de poser publiquement une question !

StackOverflow, valorise les développeurs

Une chose qui me plaît énormément dans la vie, et qui se dénote particulièrement bien dans le monde de la programmation, c'est que chacun peut être un expert à son propre niveau. J'entends par là qu'on a tous notre propre expérience, et qu'il est toujours possible, et agréable, d'en faire profiter quelqu'un d'autre. Il y a de nombreuses façons d'exprimer et de partager ses propres connaissances: à l'oral, en privé ou en public, lors d'une brain session, d'une formation, d'un live-meeting, de manière écrite, dans un blog, une revue, un bouquin, ... Et pourquoi ne pas faire ça comme un jeu, en contribuant à l'une des plus grandes communautés de développeurs?

J'ai mis un certain temps avant de répondre à ma première question sur StackOverflow. Au départ, j'avais toujours l'impression que les questions étaient trop compliquées pour moi. Ensuite, je suis tombé sur un sujet que je connaissais, pour avoir déjà rencontré le problème. J'y ai répondu maladroitement. Ma réponse n'a pas été acceptée par l'auteur de la question, mais un utilisateur a voté positivement pour dire qu'elle était du moins utile. Quelques jours plus tard, je remets le couvert avec deux nouvelles réponses (dont une qui parlait de Java :) j'ai encore de bons restes malgré tout! ), et là c'est la consécration! Les deux réponses sont acceptées par les auteurs, et je gagne 50 points de réputation!

C'est à moment-là que j'ai découvert l'autre côté de StackOverflow. Le côté le plus passionnant. Quand je réponds à des questions, par principe, je vérifie ce que je crois être juste avant de le poster. Et croyez-le ou pas, mais ça me fait apprendre énormément de choses ! Soit parce que je me rends compte de mes erreurs, soit parce que je comprends plus en profondeur le fonctionnement de l'un ou l'autre concept! Et pour finir, d'autres me récompensent d'un upvote. Pour moi, c'est extrêmement valorisant.

Je pense sincèrement que chaque développeur, ayant envie d'apprendre et de partager, peut prendre conscience, grâce à StackOverflow, du potentiel qu'il représente, et y trouver une source de satisfaction, et de confiance en soi.

De consommateur à acteur

Il ne tient qu'à vous maintenant, si ce n'est pas déjà fait, de changer de statut et de devenir créateur de contenu plutôt que consommateur. Vous êtes déjà un expert, à votre échelle. Faites-en profiter les autres. La création incite à la création.

Si vous êtes motivés, sachez que StackOverflow n'est pas le seul site du genre, vous pourrez en trouver d'autres susceptibles de vous intéresser sur StackExchange.

En espérant avoir suscité l'une ou l'autre vocation !

( Ps: prévenez-moi si vous dépassez un jour le score de Jon Skeet )

TextBox

About the author

I'm a developer, blog writer, and author, mainly focused on Microsoft technologies (but not only Smile). I'm Microsoft MVP Client Development since July 2013.

Microsoft Certified Professional

I'm currently working as an IT Evangelist with an awesome team at the Microsoft Innovation Center Belgique, where I spend time and energy helping people to develop their projects. I also give training to enthusiastic developers and organize afterworks with the help of the Belgian community.

MIC Belgique

Take a look at my first book (french only): Développez en HTML 5 pour Windows 8

Développez en HTML5 pour Windows 8

Membre de l'association Fier d'être développeur

TextBox

Month List