[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 )

[WP7] [MVVM] Construire une application étape par étape : 1/ Introduction

11. January 2012 09:01 by Renaud in   //  Tags:   //   Comments (5)

Cet article fait partie d'une longue série 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:

Le développement sera constitué de trois grandes parties, parfois subdivisées en tâches pour raccourcir les articles:

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

 Quel est le but de l'app réalisée?

MyShows (ce sera le nom de cette application) va utiliser les services de Last.FM pour fournir une liste de concerts à proximité de l'endroit où se trouve l'utilisateur, ainsi que la possibilité de consulter les concerts des artistes qu'il souhaite. On pourra donc afficher une timeline des concerts des artistes suivis, et proposer des artists similaires que l'utilisateur pourrait vouloir suivre également.

Voilà donc très brièvement la description fonctionnelle de ce que l'on va réaliser dans les articles qui suivent.

Avants-propos

Depuis un an maintenant que je développe des applications pour Windows Phone 7, j'ai rencontré bon nombres de situations à s'arracher les cheveux. Des dépendances cycliques, des problèmes de binding, des difficultés aves les requêtes asynchrones, ...

Cela était dû en grande partie à ma mauvaise connaissance de Silverlight. En fait, j'ai vraiment découvert Silverlight au moment de commencer le développement pour Windows Phone.

Avant ça, ma seule expérience .NET consistait en un projet ASP.NET MVC2, et quelques webforms. Je n'avais donc jamais entendu parler du Binding, et j'ai découvert sa magie petit à petit au cours des différents projets qui suivirent.

La raison pour laquelle j'ai décidé de faire cette série d'articles est qu'il m'est souvent arrivé récemment d'être questionné par d'autres développeurs sur "comment je pourrais faire ceci ou cela?", comme par exemple "comment retrouver le libellé de mon produit lorsque je clique sur le bouton supprimer de ce produit?". Et là, je me demande toujours comment ont-ils fait pour arriver à avoir ce genre de besoins?

Alors il y a deux réponses possibles: Soit on passe du temps à expliquer qu'il y a manifestement un problème de design dans l'application. Il faut alors, en essayant de trouver les mots justes pour ne pas faire de peine au développeur, lui conseiller de refactorer son application vite fait avant que cela ne se complique davantage. Soit on trouve un workaround mais il faut à ce moment-là garder à l'esprit que ce n'est pas quelque chose à refaire. En effet, ce n'est pas parce qu'une solution semble simple qu'il faut en user jusqu'à la corde. Et inversement, la complexité n'est pas une preuve de qualité. Je sais bien qu'on est plutôt fainéants, nous, développeurs, mais il faut savoir trouver le juste milieu entre le trop et le trop peu.

Donc dans le cas d'un petit projet, ou d'un POC, ce ne sera pas nécessaire de déployer l'artillerie lourde.

Le problème est que si l'on souhaite continuer à faire évoluer l'application, on risque à un moment ou à un autre de se retrouver fasse à une impasse. Et il sera un peu tard pour se remettre en question... Alors pour ceux qui souhaitent un projet pérenne, il vaut sans doute mieux prendre le temps de s'arrêter et de se poser les bonnes questions maintenant, pour ne pas le regretter après!

C'est pour pouvoir toucher un maximum de personnes que j'ai commencé ce nouveau projet. J'espère ainsi qu'il permettra à quelques uns d'entre vous de trouver des solutions aux problèmes auxquels vous faites face, ou bien qu'ils vous éclaireront sur un sujet peut-être encore un peu flou!

Introduction

Un peu de théorie (très rapidement :) )...

Le pattern MVVM signifie Model - View - ViewModel. Il est principalement utilisé dans les applications de type WPF, Silverlight. Si vous vous êtes déjà un peu plongé dans le développement Windows Phone, il est pratiquement impossible que vous n'en ayez jamais entendu parler ! :)

L'idée de ce pattern, est de fournir un ensemble de principes pour architecturer votre application avantageusement. Les principaux bénéfices que vous en tirerez sont:

  • Réutilisation du code
  • Dépendances faibles
  • Possibilité de travailler sur le Modèle, les Views, ou les ViewModels de manière séparée ou simultanée (facilite le travail en équipe)
  • Séparation claire des rôles. Ainsi, une personne travaillant sur les Views n'aura pas à écrire de code proprement dit. Vous pourrez donc confier ce travail à un designer.
  • Utilisation des bienfaits du Binding et des DataTemplates
  • et probablement d'autres que j'ai oublié pour le moment...

Les rôles

Voici une définition rapide des composantes du MVVM:

Model: le modèle ne correspond pas uniquement au modèle au sens propre, c'est-à-dire la représentation des données. Il sera aussi constitué des services permettant d'accéder à ces données ou de les stocker, par exemple une couche utilisant le pattern Repository (on y reviendra par la suite).

View: la vue est un point essentiel du projet, puisque c'est elle qui sera exposée à l'utilisateur. Ici, ce sont les pages XAML, qui seront chargées d'afficher les données du modèle, des informations sur l'état de ces données et de mettre à la disposition de l'utilisateur des moyen d'interagir avec ces données.  Malheureusement pour moi je n'ai aucun talent dans ce domaine, mais je ferai de mon mieux pour vous épater!

ViewModel: last but not least, le viewmodel va jouer un rôle important puisqu'il va permettre non seulement de faire appel aux services du modèle pour charger les données, mais également de décider de la manière dont les données seront exposées. Le ViewModel va ainsi lister les données qui seront affichées à l'écran, mais pas la façon de les afficher (ceci étant du domaine de la View). D'autre part, il va également se charger d'effectuer des actions et de manipuler le modèle, parfois en faisant appel à la couche service de celui-ci.

Dépendances faibles

On peut dès à présent constater la notion de dépendance faible:

Le modèle ne connait pas le ViewModel, ni les Views. Les couches de services du modèles, peuvent être exposées sous forme d'interfaces.

Le ViewModel, ne connait pas les views. Il connaîtra le modèle client proprement dit (les classes représentant les données), mais ne fera appels aux services que sur base des interfaces. Ceci laisse donc libre choix dans l'implémentation de ces services, et permettra également d'utiliser de faux services retournant des données valides pour faire des tests.

La View se servira du Binding pour utiliser les propriétés exposées par le viewmodel. Encore une fois, vous verrez ici qu'il n'y a pas de dépendance forte, car la vue ne connaît pas le type du ViewModel. La résolution ne se fera qu'au moment de l'exécution de l'application.

Let's get started

Rien ne vaut un peu de pratique pour que toutes les pièces s'assemblent alors commençons tout de suite.

Pour bien commencer, je vous suggère de créer un nouveau projet et d'y ajouter les références au MVVM Light Toolkit en utilisant NuGet. Ce framework (il en existe d'autres mais c'est celui que j'utilise régulièrement et que j'utiliserai dans les articles qui suivront) contient un ensemble d'outils qui nous permettront de mettre en place facilement une architecture MVVM!

[caption id="attachment_1082" align="aligncenter" width="765" caption="Nouveau projet Panorama avec MVVM Light Toolkit"][/caption]

Les articles suivant seront publiés régulièrement, donc n'hésitez pas à revenir de temps en temps pour voir s'il y a du nouveau. Ou suivez-moi sur Twitter pour être tenus au courant! @DumontRenaud

[wp7] #besug app for Windows Phone 7

22. November 2011 16:35 by Renaud in   //  Tags:   //   Comments (0)

In Belgium, we have the chance to have strong developers communities organizing lots of events :) Here are the user groups I'm following :

The Besug  website is of course developed in Silverlight and because of this, most smartphone users can't access it! That's why I decided to develop a Windows Phone 7 application, to be able to check where the last events will take place and what it will be talking about. It's now available on the marketplace, and you can donwload it here:

[wp7] #besug app pour Windows Phone 7

22. November 2011 10:11 by Renaud in   //  Tags:   //   Comments (0)

On a la chance en Belgique d'avoir des communautés actives de développeurs qui proposent régulièrement des events :) Parmi les user groups que je fréquente le plus, il y a :

Le site de Besug est bien entendu logiquement développé en Silverlight, ce qui le rend inaccessible pour la plupart des utilisateurs de smartphones. Du coup, j'ai décidé de faire une application Windows Phone 7 pour pouvoir consulter de temps à autres les derniers events disponibles et tant qu'à faire, je l'ai publiée sur le marketplace:

Changement d'adresse!

20. November 2011 18:11 by Renaud in   //  Tags:   //   Comments (0)

Le 23 novembre 2011, cela fera un an depuis l'achat du nom de domaine maddev.eu. Et comme mon hébergement arrivait à son terme, j'me suis dit qu'il était temps de changer tout ça.

Depuis un an donc, j'utilisais l'offre premium gratuite de Ikoula! C'est vraiment cool pour commencer, pour tester quelques trucs, pour hoster l'un ou l'autre projet web quand on est encore student et qu'on veut impressionner le prof :) Cet hébergement m'a énormément servi, c'est une super offre! Mais au final, je ne l'ai pas vraiment utilisé complètement. Il y avait je crois 5 bases mysql et 3 bases MS SQL. Autant dire qu'il y a de quoi faire.

Bref, comme je n'en avais pas vraiment l'utilité, plutôt que de repartir pour un an, j'suis passé chez OVH. J'ai donc finalement repris deux hébergement, plus adaptés niveau taille: un perso pour mon blog, qui ne servira qu'à ça. Et puis, un hébergement windows en vis-à-vis, qui pourra me servir de labo pour mes futurs expériences .NET :)

Le 23 novembre 2011, cela fera un an depuis l'achat du nom de domaine maddev.eu. Et comme mon hébergement arrivait à son terme, j'me suis dit qu'il était temps de changer tout ça.

Depuis un an donc, j'utilisais l'offre premium gratuite de Ikoula! C'est vraiment cool pour commencer, pour tester quelques trucs, pour hoster l'un ou l'autre projet web quand on est encore student et qu'on veut impressionner le prof :) Cet hébergement m'a énormément servi, c'est une super offre! Mais au final, je ne l'ai pas vraiment utilisé complètement. Il y avait je crois 5 bases mysql et 3 bases MS SQL. Autant dire qu'il y a de quoi faire.

Bref, comme je n'en avais pas vraiment l'utilité, plutôt que de repartir pour un an, j'suis passé chez OVH. J'ai donc finalement repris deux hébergement, plus adaptés niveau taille: un perso pour mon blog, qui ne servira qu'à ça. Et puis, un hébergement windows en vis-à-vis, qui pourra me servir de labo pour mes futurs expériences .NET :)

La team de stagiaires 2011-2012 du Microsoft Innovation Center

8. November 2011 22:11 by Renaud in   //  Tags:   //   Comments (1)

Comme l'année avance à grand pas, il était temps pour les students d'informatique en fin d'études de se trouver un lieu de stage! :) Je pense que tout le monde le sais maintenant, j'ai eu moi-même la chance de faire mon stage au MIC l'année dernière. Alors je crois qu'il est bon maintenant d'accueillir les nouvelles recrues comme il se doit! Ils se sont présentés là: sur le blog du Microsoft Innovation Center. J'pense qu'il est pas inutile de commencer à les suivre parce qu'entre les projets mobile sur Windows Phone Mango, les projets de robotique avec l'ami NAO, les projets Kinect, Emotive headset, et j'en passe... ça risque d'être une année très riche et intéressante :D Bref, j'leur souhaite de bien faire ça et de profiter autant que possible! Ca se termine toujours trop vite ^^ Et puis pour rester dans l'éco-système Microsoft, n'oubliez pas le Webcafé de ce 23 novembre à Louvain ;) http://www.mswebcafe.be/en/Archive/5_webcafe-leuven [spoil] Vous y croiserez sûrement l'un ou l'autre stagiaire du MIC :) [/spoil]

[CRM 2011] Gérer l'événement OnRefresh d'un subgrid avec une relation 1:N

7. November 2011 16:38 by Renaud in   //  Tags:   //   Comments (0)

Si vous utilisez un Subgrid pour afficher une liste d'entité associées, vous pourriez avoir envie d'afficher également une valeur relative à toutes ces entités! Par exemple, si on imagine une entité Facture, qui a une relation 1 à N avec une entité Détail. Sur la form Facture, on peut afficher un subgrid reprenant la liste des Détails. On peut également vouloir afficher un nouveau champs sur la facture, pour y indiquer le Montant total, qui serait la somme de l'attribut Montant de chaque Détail. Ce champs serait readonly, et calculé automatiquement sur base des Détails. Pour calculer la valeur de ce champs, il est bien entendu conseillé d'utiliser un plugin ou un workflow. Avec des plugins, et en considérant qu'une fois un Détail créé il n'est plus possible de changer la Facture associée, cela donnerait ceci:

- Message: Create / Primary entity: Détail / Execution : Post-operation
- Message: Update / Primary entity: Détail / Attributes filter: Montant / Execution : Post-operation
- Message: Delete / Primary entity: Détail / Execution: Pre-operation

Dans chacun de ces cas, le plugin devra retrouver la Facture associée à l'entité Détail, récupérer la liste des Détails associés à cette Facture, et calculer le Montant total pour le mettre à jour. La marche à suivre est assez basique: On récupère dans le context l'Id du Détail qui a déclenché l'exécution du Plugin. On va ensuite récupérer l'Id de la Facture parent. Et finalement on va récupérer la liste de tous les détails ayant cette Facture pour parent. Une fois qu'on a la liste, qui contiendra par ailleurs notre Détails fraichement mis à jour, on peut calculer le montant total de la facture. (Notez que pour le message Delete, c'est un cas particulier: on utilise le Pre-operation. Premièrement, dans ce cas, l'InputParameters du context d'exécution ne contiendra pas un objet de type Entity derrière la clé Target, mais un objet de type EntityReference. Deuxièmement, si l'on essayait de retrouver l'id de la Facture associée à ce Détail dans l'OrganizationService, une exception serait lancée car ce détail n'existerait déjà plus.) Bref, nous voici avec un champs Montant Total, correctement mis à jour à chaque modification, chaque création, ou chaque suppression de détail. Toutefois, ce n'est pas encore parfait :) Si vous ouvrez un Détail depuis le subgrid d'une Facture, que vous modifiez la valeur de son Montant, et que vous sauvez les modifications, le Montant total de la Facture restera inchangé tant que vous n'aurez pas rafraîchis la page de la Facture. Il existe toutefois une solution pour rendre le tout plus dynamique! Appelons le Subgrid "Details"! Nous allons utiliser deux évènements de ce subgrid: onreadystatechange et onrefresh. En fait, le but ici est d'appeler une méthode pour récupérer la nouvelle valeur du Montant Total de facture à chaque fois que le l'évènement onrefresh est déclenché. Le problème est que ce subgrid est chargé de manière asyncrhone! On va appeler la méthode ci-dessous une fois la fenêtre loadée. On va d'abord vérifier l'état actuel du grid. S'il est déjà complètement chargé, on s'abonne à l'évènement onrefresh. Dans le cas contraire, on va attacher une fonction à l'évènement onreadystatechange, pour être sûr qu'une fois la grille complètement chargée l'eventhandler UpdateTotalAmount soit attaché à l'event onrefresh.

function Subpolicies_OnReadyRefresh(){
    var targetgrid = document.getElementById("Details");

    // If already loaded
    if (targetgrid.readyState == 'complete')
    {
        targetgrid.attachEvent("onrefresh", UpdateTotalAmount);
    }
    else
    {
        targetgrid.onreadystatechange = function applyRefreshEvent() {
            var targetgrid = document.getElementById("Details");
            if (targetgrid.readyState == 'complete') {
                targetgrid.attachEvent("onrefresh", UpdateTotalAmount);
            }
        }
    }
}

Il ne vous reste plus qu'à implémenter la méthode UpdateTotalAmount pour récupérer l'attribut Montant Total de la Facture :) Sources: http://blog.xrm-services.co.uk/?p=150

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