Mise en œuvre de l’outil de déploiement web IIS (Microsoft Web Deploy 3 .0) – Partie 1

by Charles BARJANSKY 19. février 2013 02:18

Préambule

J’entame avec cet article une série de quatre articles pour vous présenter la solution d’automatisation de publication d’application Internet (ASP .NET, ASP .NET MVC, Silverlight, etc...) sur un serveur IIS.

Les quatre parties sont :

  • Installation de Microsoft Web Deploy (sur une instance Windows Server ayant le rôle IIS) et publication d’une application à partir de Microsoft Visual Studio 2012
  • Publication d’une base de données
  • Sauvegarde de l’application lors de la publication
  • Publication d’une application lors d’un build TFS (TFS 2012)

Le but de l’ensemble de ces articles est, bien entendu, de mettre en œuvre les bonnes pratiques en ce qui concerne l’un des pans de l’ALM (Application Lifecycle Management) : le déploiement d’une application.

Objectif de l’article "Mise en œuvre de l’outil de déploiement IIS (Microsoft Web Deploy 3 .0) – Partie 1"

L’objectif de cette première partie est de vulgariser la mise en œuvre de l’outil de déploiement IIS (Microsoft Web Deploy) et la publication d’une application Internet via la méthode "déploiement web" (Web Deployment) à partir de Visual Studio sur un serveur IIS.

Cette mise en œuvre comprend :

  1. L’installation de l’outil Microsoft Web Deploy 3.0 est un prérequis pour permettre la mise en place de la publication web d’une application Internet.
  2. La création d’un utilisateur IIS pour s’affranchir des problèmes de gestion d’un utilisateur Windows dans une infrastructure hétérogène.
    "Vous pouvez ajouter un compte d'utilisateur du Gestionnaire des services Internet (IIS) dans le Gestionnaire des services Internet (IIS) si vous souhaitez autoriser un utilisateur à se connecter à un site ou à une application du serveur, mais ne souhaitez pas créer un compte d'utilisateur Windows ou ajouter l'utilisateur dans un groupe Windows. Les informations d'identification du Gestionnaire des services Internet (IIS) se composent d'un nom d'utilisateur et d'un mot de passe qui sont créés dans le Gestionnaire des services Internet (IIS) et sont utilisés exclusivement pour que le Gestionnaire des services Internet (IIS) accède aux fichiers de configuration IIS."
    (source : http://technet.microsoft.com/fr-fr/library/cc732621%28v=ws.10%29.aspx)
  3. La création d’une règle de délégation qui ajoute des autorisations à un utilisateur (Windows ou IIS) de gestion de un ou plusieurs sites IIS ou de une ou plusieurs applications.
  4. L’activation de la fonctionnalité de déploiement web sur un site IIS permet:
    • D’activer l’option de déploiement web sur un site IIS
    • De désigner l’utilisateur (Windows ou IIS) qui sera autorisé à déployer
    • De générer un fichier .Publishsettings
  5. La publication d’une application Internet partir de Visual Studio grâce au fichier .PublishSettings. Dès lors, nous pourrons déployer une application Internet vers une instance IIS à partir de Microsoft Visual Studio en moins de 10 clics de souris...

Installation de l’outil de déploiement (Web Deploy 3.0)

Pré-Requis

Les pré requis pour permettre cette mise en œuvre sont :

Mise en œuvre pas à pas

  1. Exécuter l’exécutable "WebDeploy_x[platefom]_xx_xx.msi"
  2. A l’étape de démarrage "Bienvenue dans l’Assistant d’installation de Microsoft Web Deploy 3.0" ("Welcome to the Microsoft Web Deploy 3.0 Setup Wizard"), démarrer l’installation de l’outil en cliquant sur "Suivant" ("Next")
    Microsoft Web Deploy 1
  3. A l’étape "Contrat de Licence Utilisateur Final" ("End-User Licence Agreement"), accepter les termes et cliquer sur "Suivant" ("Next")
    Microsoft Web Deploy 2
  4. A l’étape "Choisissez un type d’installation" ("Choose Setup Type"), cliquer sur "Complète" ("Complete")
    Microsoft Web Deploy 3
  5. A l’étape "Prêt à installer Microsoft Web Deploy 3.0", cliquer sur "Installer" ("Install")
    Microsoft Web Deploy 4
  6. Patienter lors de l’installation du composant
    Microsoft Web Deploy 5
  7. A la fin de l’assistant, cliquer sur "Terminer" ("Finish")
    Microsoft Web Deploy 6 

Paramétrage de l’outil de déploiement web

Création d’un utilisateur IIS

  1. Dans la console "Gestionnaire des services Internet (IIS)" ("Internet Information Services (IIS)"),
    1. Sélectionner le serveur désiré
    2. Dans la vue "Affichage des fonctionnalités" ("Features View") > "Gestion" ("Management"), sélectionner l’élément "Utilisateurs du Gestionnaire des services Internet" ("IIS Manager User")
    Utilisateur 1
  2. Dans la vue "Utilisateurs du Gestionnaire des services Internet" ("IIS Manager User"), cliquer sur "Ajouter un utilisateur..." ("Add User... ")
    Utilisateur IIS 2
  3. Spécifier un "Nom d’utilisateur" ("User name"), un "Mot de passe" ("Password") et cliquer sur le bouton "OK"

    Utilisateur IIS 3

Création d’une délégation du service de gestion

  1. Dans la console "Gestionnaire des services Internet (IIS)" ("Internet Information Services (IIS)"),
    1. Sélectionner le serveur désiré
    2. Dans la vue "Affichage des fonctionnalités" ("Features View") > "Gestion" ("Management"), sélectionner l’élément "Délégation du service de gestion" Management Service Delegation")
    Delegation IIS 1
  2. Dans la vue "Délégation du service de gestion" ("Management Service Delegation"), cliquer sur "Ajouter une règle... " (" Add Rule... ")
    Delegation IIS 2
  3. Dans la fenêtre "Ajouter une règle" ("Add Rule"), sélectionner "Règle vide" ("Blank rule") et cliquer sur "OK"
    Delegation IIS 3
  4. Dans la fenêtre "Ajouter une règle" ("Add Rule"),
    1. fournir les informations suivantes :
      1. "Fournisseurs" ("Providers")
        1. "contentPath"
        2. "createApp"
        3. "iisApp"
        4. "setAcl"
      2. "Actions"
        1. "*"
      3. "Type de chemin" ("Path Type")
        1. "Préfixe du chemin d’accès" ("Prefix Path")
      4. "Chemin d’accès" ("Path")
        1. "{userScope}"
      5. "Type d’identité" ("Identity Type")
        1. "Current User"
    2. Cliquer sur "OK"
    Delegation IIS 4
  5. Dans la fenêtre "Ajouter un utilisateur à la règle" ("Add User To Rule"), saisir le nom de l’utilisateur IIS dans le champ "Nom" ("Name") puis cliquer sur "OK".
    Delegation IIS 5
  6. La règle de délégation est créée avec l’utilisateur associé
    Delegation IIS 5

Activer la publication Web Deploy pour un site IIS

  1. Dans la console "Gestionnaire des services Internet (IIS)" ("Internet Information Services (IIS)"),
    1. Sélectionner le serveur désiré
    2. Déployer le nœud "Sites"
    3. Effectuer un clic droit sur le site sur lequel vous souhaitez activer l’option
    4. Sélectionner "Déployer" ("Deploy") puis "Activer la publication Web Deploy" ("Configure Web Deploy Publishing")
    Activation 1
  2. La fenêtre "Activer la publication Web Deploy" ("Configure Web Deploy Publishing") s’affiche. A l’emplacement "Sélectionner un utilisateur auquel accorder les autorisations de publication", cliquer sur le bouton "..."
    Activation 2
  3. Dans la fenêtre "Autoriser l’utilisateur..." ("Allow User..."), sélectionner la pastille "Gestionnaire des services Internet" ("IIS Manager"), sélectionner l’utilisateur désiré et cliquer sur "OK"
    Activation 3
  4. Dans la fenêtre "Activer la publication Web Deploy" ("Configure Web Deploy Publishing"), vérifier les différentes informations et cliquer sur "Configurer" ("Setup")
    Activation 4
  5. Vérifier le résultat puis cliquer sur "Fermer" ("Close")
    Activation 5
  6. Vous pouvez désormais rendre disponible le fichier généré à la personne désirée
    Activation 6

Publication de l’application Internet (Visual Studio 2012)

  1. Dans votre solution Visual Studio 2012, effectuer un clic droit sur le projet Web et sélectionner l’option "Publish..."
    Visual Studio 1
  2. L’assistant "Publish Web" s’affiche. Cliquer sur le bouton "Importer..." ("Import...") et sélectionner le fichier généré avec l’extension .publish.
    clip_image004[10]
  3. A l’étape "Connexion" ("Connection"), saisir le mot de passe, lancer la validation des informations via le bouton "Valider la connection" ("Validate Connection") puis cliquer sur "Publier" ("Publish")
    clip_image006[10]
  4. A la fin de la publication, le navigateur est lancé pour permettre de vérifier le bon fonctionnement du site.
    clip_image008[10]

Remarques

Vérification du type de démarrage du service "Service de gestion Web" ("Web Management Service")

Pour permettre le bon fonctionnement de l’outil de déploiement, vérifier le type de démarrage (Startup Type) du service "Service de gestion Web" ("Web Management Service") est spécifié à "Automatique (début différé)" ("Automatic (Delayed Start)")

Autres 1

Règle de pare-feu

Pour permettre le bon fonctionnement du service de gestion IIS, il faut s’assurer que le port 8172 est correctement configuré.
Dans le cas inverse, exécuter cette commande dans une console Powershell avec le jeton "Administrateur" ("Run As Administrator"):

New-NetFirewallRule -DisplayName "Allow IIS Management Service In" -Direction Inbound -LocalPort 8172 -Protocol TCP -Action Allow

Activation du suivi des requêtes échouées

Vous pouvez activer le suivi des requêtes échoués si vous rencontrez des difficultés lors du paramétrage de l’outil.
Vous pouvez activer l’option "Activer le suivi de requêtes échouées" ("Enable failed request tracing") dans la vue "Service de gestion" ("Management Service").

Autres 2

Dès lors, vous pouvez consulter les différentes tentatives de publication échoués à l’emplacement suivant :

%SystemDrive%\inetpub\logs\wmsvc\TracingLogFiles

Liste des "fournisseurs de déploiement web" ("Web Deploy Providers")

La liste des fournisseurs ainsi que leur description est disponible à l’emplacement suivant : http://technet.microsoft.com/fr-fr/library/dd569040.aspx

 

A bientôt!

Tags: , , ,

Développement | Infrastructure

BizTalk - Bug sur les Delivery Notifications (BizTalk 2006 R2 et 2009) – Part 1

by Simon Rivas 1. février 2013 02:38

J’ai dernièrement rencontré un problème assez déroutant chez un client utilisant les delivery notifications (DN) sur une plateforme BizTalk 2006 R2

 

Rappel : principe des delivery notifications

Mécanisme général

BizTalk a la capacité de publier automatiquement des acquittements positifs (ACK) en cas de transmission réussie d’un message et des acquittements négatifs (NACK) en cas d’erreur. Les orchestrations peuvent utiliser les DN pour s’abonner aux acquittements pour les messages qu’elles envoient.

L’activation des DN se fait de 2 manières :

  • La plus courante est de positionner la propriété Delivery Notification d’un port logique d’envoi (dans une orchestration) à Transmitted.

image

  • Une autre méthode est de positionner la propriété de contexte BTS.AckRequired à true sur le message à envoyer.

image

NOTE : fondamentalement, les deux méthodes sont identiques puisque le positionnement de la propriété Delivery Notification à Transmitted au niveau du port indique simplement à BizTalk qu’il faut positionner la propriété BTS.AckRequired à true sur tous les messages transitant par le port.

Propriétés de contexte associées

Les ACKs et NACKs ont chacun un jeu de propriétés de contexte promues associé, qui sont les suivantes :

Propriété Description
AckType Vaut ACK en cas de succès ou NACK en cas d’erreur
AckID MessageID du message auquel est associé l’acquittement
AckOwnerID InstanceID de l’instance à laquelle est associé l’acquittement
AckSendPortID ID du port par lequel le message a été envoyé
AckSendPortName Nom du port par lequel le message a été envoyé
AckOutboundTransportLocation URL du port par lequel le message a été envoyé
AckReceivePortID  ID du port par lequel le message est arrivé
AckReceivePortName Nom du port par lequel le message est arrivé
AckInboundTransportLocation URL du port de réception

Fonctionnement détaillé

La publication des acquittements présente une particularité par rapport au fonctionnement standard de la Message Box : en effet si aucune souscription active n’existe pour un acquittement, celui-ci est simplement ignoré. La conséquence de ceci est qu’il ne peut pas y avoir d’erreur de routage sur un acquittement et qu’un acquittement ne peut être suspendu.

D’autre part, lorsqu’un message est envoyé sur un port d’envoi pour lequel la propriété Delivery Notification est positionnée à Transmitted, un correlation set est initialisé, et un jeton de correlation propriété CorrelationToken) est associé au message sortant, et sera également associée à l’acquittement (la propriété sera également promue). Lorsque l’acquittement est créé, il est automatiquement routé vers l’orchestration appelante.

Enfin lorsque l’orchestration appelante reçoit un ACK, l’orchestration sort du Scope dans lequel elle était arrêtée et reprend ses traitements suivants. Lorsqu’elle reçoit un NACK, une exception de type DeliveryFailureException est générée, qui peut alors être interceptée dans une section Catch.

 

Contexte du problème

Le contexte est le suivant :

  • Dans une orchestration, les DN sont activés sur un port d’envoi unidirectionnel
  • En cas d’erreur, l’instance de messaging est suspendue et l’orchestration intercepte bien une exception (DeliveryFailureException), le mécanisme fonctionne donc comme prévu jusque-là.
  • Dès interception de l’exception, l’orchestration est programmatiquement terminée (au moyen d’une shape Terminate)

A ce stade, il reste donc uniquement l’instance de messaging suspendue, en attente d’être résumée.

Symptômes

Une fois le problème à l’origine de l’erreur (dans le cas de mon client : les droits sur un site FTP avaient sauté), on résume l’instance de messaging.

Le comportement attendu est que cette instance passe au statut Completed, ne laissant ainsi plus aucune instance active ou suspendue.

Or l’instance de messaging est à nouveau suspendue mais pour cause d’erreur de routage, résultant donc en une instance suspendue résumable correspondant au message lui-même et une instance suspendue non résumable correspondant au rapport d’erreur de routage :

image

Le rapport d’erreur de routage nous apprend que les propriétés suivantes sont attachées au message :

image

 

Diagnostic

Les propriétés attachées au message nous indiquent que c’est tout simplement un acquittement (ACK dans notre cas) qui a été publié dans la Message Box et qui a été suspendu du fait qu’aucune souscription active n’a été trouvée.

En effet : l’orchestration appelante s’est terminée après avoir reçu un premier NACK. Puisqu’elle est terminée, sa souscription sur les acquittements n’est plus active, et le ACK ne peut être routé vers aucune instance active. Le résultat est donc une erreur de routage.

Or ce fonctionnement est en contradiction avec le principe des DN (cf plus haut) : un acquittement ne peut pas être suspendu, il devrait être ignoré.

Cause

Il s’agit bien d’un bug, apparu avec le SP1 de BizTalk 2006 R2. Il est passé au travers des 4 Cumulative Updates suivants.

Par curiosité, j’ai effectué le test sur une plateforme BizTalk Server 2009 sur laquelle le CU6 avait été appliqué, le bug était toujours présent… Reste à élargir les tests aux versions 2010 et 2013.

Solution

Il n’existe donc aucun correctif, mais on peut mettre en place une solution de contournement. Celle-ci consiste à créer une orchestration s’abonnant sur un message de type XmlDocument pour lequel la propriété AckType existe, le port de réception étant en binding direct sur la Message Box. Ainsi cette orchestration consommera systématiquement tous les acquittements, ACKs comme NACKs, ce qui évitera toute erreur de routage. L’orchestration serait comme suit :

image

D’autre part, de manière à ne pas polluer le HAT et la DTADb (après tout, cette orchestration ne fait que se substituer à la mécanique interne de BizTalk, il ne devrait donc pas y avoir de traces de son fonctionnement), il suffit de désactiver tout le tracking :

image

Néanmoins cette solution ne résout pas tout, comme nous le verrons dans un autre article (BizTalk - Bug sur les Delivery Notifications (BizTalk 2006 R2 et 2009) – Part 2), à paraitre.

Tags: , , ,

Biztalk

Créer sa première application Windows Store avec C# XAML

by acamara 16. octobre 2012 00:30

Introduction

Comment créer sa première application Modern UI, avec C# XAML?

Les applications Modern UI connaissent un véritable essor dans le monde du développement. Notamment, avec l'arrivée imminente du nouvel OS Windows 8 de chez Microsoft, ainsi qu'une multitude d’appareils et de supports tactiles, sur lesquels nous allons justement pouvoir utiliser ces applications.

Sur la toile, vous trouverez un bon nombre de tutoriels pour commencer à développer une application pour Windows 8. Je pense au blog de Loïc Rebours, que je trouve complet.

Dans cette article, j’insisterai sur la manière dont nous pouvons développer une application, en utilisant le pattern MVVM, au travers d’un exemple concret. Puis, dans un second article, je vous présenterai une manière de déployer une application sans passer par le Windows Store. Quels genres de contraintes et de restrictions allons-nous rencontrer ?

 

Prérequis

Avant de rentrer dans le vif du sujet, nous allons commencer par les prérequis.

Pour concevoir des applications Windows Store, il faut :

  • être doté d’une machine équipée de l’OS Windows 8, et d’un compte Microsoft (nécessaire et pour faciliter les déploiements)
  • avoir une licence Développeur Microsoft (gratuite) ; cette licence est proposée lors de la première compilation d’un projet Windows Store
  • être équipé de l’IDE Visual Studio 2012,  avec le Framework 4.5 (à minima)

Contexte

Notre application consiste à afficher le profil d’un utilisateur depuis un back-end existant :

 

Dans le monde de l’entreprise, on a souvent besoin de récupérer les informations depuis une base de données ou un Active Directory, par exemple.

Pour rappel, une application Modern UI évolue dans un  environnement dit « Sandbox ». C’est-à-dire qu’une telle application ne peut communiquer directement avec une application tierce.

Les applications Windows Store sont soumises à des restrictions. Et, par conséquent, elles ne peuvent interagir directement avec une base de données ou un annuaire LDAP. Pour cela, la solution du Web service va nous permettre de répondre à notre besoin.

Le but de cet article n’est pas d’apprendre comment créer un Web Service avec WCF. Je pourrais très bien créer une « data source », pour simplifier les développements. Mais, je pense qu’on passerait à côté de certaines informations.

 

Préliminaires

Nous sommes partis du principe que la partie Middleware et Back-end existaient déjà. En effet, cette partie a été précédemment développée pour les besoins d’une application interne. Il s’agit du trombinoscope de Neos-SDI développé à partir d’un des templates de Windows Store Apps (« Je dis ça, je dis rien ! »).

Voici, la solution finale avec la logique MVVM. Cette solution sera disponible en interne. 

 

Qu’est-ce que le MVVM ?

Le MVVM est un « pattern »  de conception architecturale qui permet une séparation indépendante entre les couches :

  • métier : le Model
  • de présentation : la View
  • et, on va dire le contrôleur (par analogie au design pattern MVC) : la ViewModel ; cette dernière couche va jouer le rôle du liant entre la couche métier et la couche de présentation.

 

Pour bien commencer…

Avant de mettre en place l’architecture MVVM dans notre projet, souvenez-vous qu’une application Windows Store est une application Sandbox (restrictive). Une application est soumise à des contraintes déclaratives.

Pour pouvoir communiquer avec l’extérieur, ou consommer un Web Service, il va donc falloir déclarer cette fonctionnalité dans notre application. Cela se passe dans notre fichier de manifeste, Package.appxmanifest (situé à la racine de notre projet) :

NB : Pour pouvoir faire évoluer notre application et gérer les ressources de déploiement, il faut donc passer par ce fichier de manifeste.

 
Rentrons dans le vif du sujet, il va y avoir du code…

Dans la famille MVVM, je demande le…

 ...Model :

 

 

Le Model  représente les objets métier de notre back-end (cf. Contexte).  Ici, notre source de données provient de l’annuaire LDAP de notre société. Pour notre besoin, la classe Neostee représente un utilisateur commun à Neos-SDI. Cette classe est visible dans notre projet, si et seulement si nous intégrons la référence au Web Service (Middleware).

NB : Cette classe hérite d’une classe de base qui implémente l’interface INotifyPropertyChanged. Nous verrons un exemple de code, par la suite.
 

Dans la famille ViewModel, je demande le…

ViewModel :

 

 

Le ViewModel représente la liaison entre le Model et la View. L’idée est d’implémenter aussi l’interface INotifyPropertyChanged (comme dans le Model) afin d’utiliser le mécanisme de « Binding » entre la vue et l’objet métier. Nous verrons comment faire le lien entre nos objets métier et la vue.
 

Pour détailler la classe ViewModelBase, comme pour la classe ModelBase, voici une implémentation de l’interface INotifyPropertyChanged :

 

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Threading.Tasks;

namespace AppLoadProfil.ViewModel
{
    public abstract class ViewModelBase : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;

        public void OnChangedProperty(string propertyName)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }

        public void OnChangedProperty<T>(Expression<Func<T>> expression)
        {
            MemberExpression body = expression.Body as MemberExpression;

            if (body == null)
            {
                throw new ArgumentException("erreur dans l'expression");
            }

            string propertyName = body.Member.Name;

            if (!string.IsNullOrEmpty(propertyName))
            {
                OnChangedProperty(propertyName);
            }
        }
    }
}

La classe de base implémente l’évènement : event PropertyChangedEventHandler PropertyChanged; 

Les 2 surcharges de méthodes OnChangedProperty permettent de détecter le changement de valeur d’une propriété rattachée à notre ViewModel (ou notre Model).

 

Comment allons-nous utiliser cette classe de base ?

La classe ProfilViewModel hérite de cette classe de base 

 

using AppLoadProfil.BusinessLayer.Impl;
using AppLoadProfil.BusinessLayer.Interfaces;
using AppLoadProfil.Common;
using AppLoadProfil.LdapService;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Windows.Storage;
using Windows.Storage.Streams;
using Windows.UI.Popups;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Media.Imaging;

namespace AppLoadProfil.ViewModel
{
    public class ProfilViewModel : ViewModelBase
    {

        #region properties

        private Neostee _neostee = null;

        public Neostee TheNeostee
        {
            get { return _neostee; }
            set
            {
                _neostee = value;
                //base.OnChangedProperty("TheNeostee");
                base.OnChangedProperty<Neostee>(() => this.TheNeostee);
            }
        }

        private string _commonName;

        public string CommonName
        {
            get { return _commonName; }
            set
            {
                _commonName = value;
                base.OnChangedProperty("CommonName");
            }
        }


        private ObservableCollection<Neostee> _Neostees = null;

        public ObservableCollection<Neostee> Neostees
        {
            get { return _Neostees; }
            set
            {
                _Neostees = value;
                base.OnChangedProperty("Neostees");
            }
        }
        #endregion

        #region ctors
        public ProfilViewModel()
        {
            _Neostees = new ObservableCollection<Neostee>();
            LoadAsync();
        }
        #endregion

        #region private methods

        /// <summary>
        /// Méthode asynchrone qui permet le chargement du profil du neostee
        /// </summary>
        private async void LoadAsync()
        {
            string firstName = await Windows.System.UserProfile.UserInformation.GetFirstNameAsync();
            string name = await Windows.System.UserProfile.UserInformation.GetLastNameAsync();
            var commonName = "Amadou Camara";
            byte[] flux = null;
            StorageFolder folder = null;

            try
            {
                folder = Windows.ApplicationModel.Package.Current.InstalledLocation;
            }
            catch (Exception ex)
            {
                throw new Exception(ex.Message);
            }

            StorageFile file = await folder.GetFileAsync(@"Assets\SmallLogo.png");

            if (file != null)
            {
                flux = await GetByteFromFile(file);
            }

            var defaultNeostee = new Neostee() { CommonName = "Inconnu", DepartmentName = string.Empty, Description = string.Empty, Email = string.Empty, Login = string.Empty, Manager = string.Empty, ThumbnailPhoto = flux };

            if (!string.IsNullOrEmpty(firstName) && !string.IsNullOrEmpty(name))
            {
                commonName = string.Format("{0} {1}", firstName, name);
            }
            else
            {
                TheNeostee = defaultNeostee;
                _Neostees.Add(defaultNeostee);
                return;
            }

            INeosteeRepository _repository = new NeosteeRepository();

            try
            {
                var result = await _repository.Get(commonName);
                if (result != null)
                {
                    foreach (var item in result)
                    {
                        _Neostees.Add(item);
                        if (TheNeostee == null)//le 1er neostee qui correspond au critère de recherche
                        {
                            TheNeostee = item;
                            CommonName = _neostee.CommonName;
                            break;
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                throw new Exception("erreur dans la methode LoadAsync", ex);
            }
        }

        private async Task<byte[]> GetByteFromFile(StorageFile storageFile)
        {
            var stream = await storageFile.OpenReadAsync();

            using (var dataReader = new DataReader(stream))
            {
                var bytes = new byte[stream.Size];
                await dataReader.LoadAsync((uint)stream.Size);
                dataReader.ReadBytes(bytes);

                return bytes;
            }
        }
        #endregion
    }
}

 

Dans cette classe, nous allons définir l’ensemble des propriétés que nous souhaitons afficher dans la vue (la couche de présentation).

Il est même possible de définir des actions telles que les ICommand, qui représentent une action (une interaction), lors d’un évènement provoqué par utilisateur, tel que le clic sur un bouton. Mais ceci est une autre histoire.

Dans notre cas, j’ai défini  dans le ViewModel une méthode private async void LoadAsync()

Cette méthode permet de faire appel asynchrone au Web service.

Mais attention, je ne fais pas appel directement à ce Web service. Je passe par une couche métier :

 

 Cette classe est  responsable de la pertinence des données et va nous permettre de renseigner les propriétés à afficher dans la Vue. Cette classe définit une méthode Get qui va renvoyer les informations sur un employé de Neos-SDI en fonction de son Common Name.

 Dans la propriété TheNeostee, j’ai volontairement commenté la première surcharge de la méthode de base OnChangedProperty, au profit de la deuxième surcharge :  

 

private Neostee _neostee = null;

public Neostee TheNeostee
{
	get { return _neostee; }
	set
	{
		_neostee = value;
		//base.OnChangedProperty("TheNeostee");
		base.OnChangedProperty<Neostee>(() => this.TheNeostee);
	}
}

 

Cette surcharge prend en paramètre de type de la forme Lambda Expression. Cette méthode comporte ainsi moins de risques d’erreur de saisie, comparé à la méthode de base qui prend une chaîne de caractères en paramètres.  

Dans la famille ViewModel, je demande la…

View :

Pour afficher les données, on se base sur le concept lié au pattern MVVM : le databinding.

Ce mécanisme permet de lier une donnée entre notre couche de présentation et le ViewModel. Pour cela, il faut définir le contexte du ViewModel au niveau de la Vue et déclarer notre objet métier à l’aide du mot clé Binding.

Voici une vue partielle de la View, avec la déclaration du contexte pour le ViewModel :

 

 Voici une vue partielle de la page qui montre comment la « binder » avec notre objet métier TheNeostee.

Et la partie que je trouve intéressante dans cette vue :

Comment afficher une image dans notre vue ? Sachant que la propriété TheNeostee.ThumbnailPhoto est homogène à un flux binaire.

La solution est de passer par la classe ImageConverter :

  

 

La classe ImageConverter implémente l’interface IValueConverter qui définit 2 méthodes Converter et ConverterBack. Seule la première méthode est implémentée pour justement convertir notre source de données (qui provient de l’annuaire) en objet de type WriteableBitmap qui permettra de charger l’image dans notre application Windows Store.

        

using AppLoadProfil.Common;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Data;
using AppLoadProfil.StreamExtensions;
using Windows.UI.Xaml.Media.Imaging;

namespace AppLoadProfil.Converters
{
    public class ImageConverter : IValueConverter
    {
        WriteableBitmap _imageSource = null;
        private readonly int _pixelWidth = 1024;
        private readonly int _pixelHeight = 1024;

        /// <summary>
        /// to load an image from byte[] stream
        /// </summary>
        /// <param name="value">value which as byte[]</param>
        private async void LoadImage(object value)
        {
            var flux = value as byte[];
            
            if (flux != null)
            {
                _imageSource = new WriteableBitmap(_pixelWidth, _pixelHeight);
                var stream = flux.ToAccessStream();
                await _imageSource.SetSourceAsync(stream);
            }
        }

        public object Convert(object value, Type targetType, object parameter, string language)
        {
            LoadImage(value);
            return _imageSource;
        }

        public object ConvertBack(object value, Type targetType, object parameter, string language)
        {
            throw new NotImplementedException();
        }
    }
}

 

La méthode ToAccessStream est une méthode d’extension qui va renvoyer un objet de type IRandomAccessStream : interface de gestion des flux pour les applications Modern UI.

using AppLoadProfil.Common;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Windows.Storage.Streams;

namespace AppLoadProfil.StreamExtensions
{
    public static class MemoryStreamExtensions
    {
        public static IRandomAccessStream ToAccessStream(this byte[] flux)
        {
            return new ImageStream(flux);
        }
    }
}

 

Cette méthode d’extension renvoie une instance de la classe ImageStream qui prend en charge une implémentation de l’interface IRandomAccessStream :

 

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Windows.Storage.Streams;

namespace AppLoadProfil.Common
{
    public class ImageStream : IRandomAccessStream
    {
        private MemoryStream _memoryStream = null;
        public ImageStream()
        {

        }

        public ImageStream(byte[] inputStream)
        {
            if (inputStream != null)
            {
                _memoryStream = new MemoryStream(inputStream, 0, inputStream.Count(), true);
            }
        }

        public bool CanRead
        {
            get
            {
                if (_memoryStream != null)
                {
                    return _memoryStream.CanRead;
                }
                else
                {
                    return false;
                }
            }
        }

        public bool CanWrite
        {
            get
            {
                if (_memoryStream != null)
                {
                    return _memoryStream.CanWrite;
                }
                else
                {
                    return false;
                }
            }
        }

        public IRandomAccessStream CloneStream()
        {
            return this.MemberwiseClone() as IRandomAccessStream;
        }

        public IInputStream GetInputStreamAt(ulong position)
        {
            _memoryStream.Position = (long)position;
            return _memoryStream.AsInputStream();
        }

        public IOutputStream GetOutputStreamAt(ulong position)
        {
            _memoryStream.Position = (long)position;
            return _memoryStream.AsOutputStream();
        }

        public ulong Position
        {
            get { return (ulong)_memoryStream.Position; }
        }

        public void Seek(ulong position)
        {
            _memoryStream.Seek((long)position, SeekOrigin.Begin);
        }

        public ulong Size
        {
            get
            {
                return (ulong)_memoryStream.Length;
            }
            set
            {
                _memoryStream.SetLength((long)value);
            }
        }

        public void Dispose()
        {
            _memoryStream.Dispose();
        }

        public Windows.Foundation.IAsyncOperationWithProgress<IBuffer, uint> ReadAsync(IBuffer buffer, uint count, InputStreamOptions options)
        {
            var inputStream = this.GetInputStreamAt(0);
            return inputStream.ReadAsync(buffer, count, options);
        }

        public Windows.Foundation.IAsyncOperation<bool> FlushAsync()
        {
            var outputStream = this.GetOutputStreamAt(0);
            return outputStream.FlushAsync();
        }

        public Windows.Foundation.IAsyncOperationWithProgress<uint, uint> WriteAsync(IBuffer buffer)
        {
            var outputStream = this.GetOutputStreamAt(0);
            return outputStream.WriteAsync(buffer);
        }
    }
}

 

Et voilà, nous savons comment utiliser le pattern MVVM dans un projet Windows Store. Ce pattern a pour avantage de séparer les concernes et permet le travail avec un designer pour l’interface utilisateur.

Pour aller plus loin, comment naviguer avec le pattern MVVM ? Voici une première réponse proposé par Microsoft Services

Tags: , , ,

Développement

Contrôle WPF Bing Maps (2/2) : Utilisation du Bing Maps REST Services pour la géolocalisation et du Bing Maps Spatial Data Services pour la recherche de POI

by COlivier 12. octobre 2012 00:03

Le Bings Maps REST Services est une API de géolocalisation vous permettant de trouver un emplacement à partir de son adresse (ou de ses coordonnées GPS) et d’obtenir les données de localisation de l’emplacement recherché.

Le Bing Maps Spatial Data Services est une API de rechercher des POI (points d’intérêts) autour d’une localisation et d’obtenir les données de chaque POI tels que les coordonnées GPS, le nom, l’adresse et le  type de POI.

NB : Pour utiliser l’une de ces API, vous devrez obligatoirement posséder une clé Bing Maps (voir le billet intitulé «Contrôle WPF Bing Maps (1/2) : Présentation de l’intégration du contrôle dans votre application WPF »).

1/. Bing Maps REST Services

L’exemple de code ci-dessous, vous présente une méthode vous permettant de géolocaliser une adresse (telles que « Rue Auber, Paris », « Toulouse », etc.) et d’obtenir ses coordonnées GPS.

 

using Microsoft.Maps.MapControl.WPF;

/// <summary>
/// Permet de géocaliser une adresse (Ressource : http://msdn.microsoft.com/en-us/library/hh757509.aspx)
/// </summary>
/// <param name="addressQuery">Adresse à rechercher (par exemples: "Rue Auber, Paris" ou "Toulouse")</param>
/// <returns>Localisation de l'adresse recherchée (coordonnées GPS)</returns> 
public static Location GeocodeAddress(string addressQuery)
{
	Location addressLatitudeLongitude = null;

	// Création de la requête de géocalisation
	string geocodeRequest = string.Format("http://dev.virtualearth.net/REST/v1/Locations/{0}?o=xml&key={1}", addressQuery, YOU_BING_MAPS_KEY);

	//Envoi de la requête et récupèration de la réponse sous forme de document XML
	XmlDocument geocodeResponse = GetXmlResponse(geocodeRequest);
	if(geocodeResponse.IsNotNull())
	{
		//Création d'un namespace pour lire le document
		XmlNamespaceManager nsmgr = new XmlNamespaceManager(geocodeResponse.NameTable);
		nsmgr.AddNamespace("rest", "http://schemas.microsoft.com/search/local/ws/rest/v1");

		//On récupère tous les noeuds "Location" 
		XmlNodeList locationElements = geocodeResponse.SelectNodes("//rest:Location", nsmgr);
		if (locationElements.IsNotNull())
		{
			// On récupère les coordonnées de l'adresse recherchée
			XmlNodeList displayGeocodePoints = locationElements[0].SelectNodes(".//rest:GeocodePoint/rest:UsageType[.='Display']/parent::node()", nsmgr);
			string latitude = displayGeocodePoints[0].SelectSingleNode(".//rest:Latitude", nsmgr).InnerText;
			string longitude = displayGeocodePoints[0].SelectSingleNode(".//rest:Longitude", nsmgr).InnerText;

			addressLatitudeLongitude = new Location(Convert.ToDouble(latitude), Convert.ToDouble(longitude));
		}
	}
	
	return addressLatitudeLongitude;
}

/// <summary>
/// Permet d'obtenir la réponse d'une requête
/// </summary>
/// <param name="requestUrl">Requête à envoyer</param>
/// <returns>Réponse de la requête au format XML</returns>
private static XmlDocument GetXmlResponse(string requestUrl)
{
	XmlDocument xmlDoc = null;
	
	HttpWebRequest request = WebRequest.Create(requestUrl) as HttpWebRequest;
	using (HttpWebResponse response = request.GetResponse() as HttpWebResponse)
	{
		if (response.StatusCode != HttpStatusCode.OK)
		{
			throw new Exception(String.Format("Server error (HTTP {0}: {1}). Request URL (XML): {2}", response.StatusCode, response.StatusDescription, requestUrl));
		}
		xmlDoc = new XmlDocument();
		xmlDoc.Load(response.GetResponseStream());	
	}
	
	return xmlDoc;
}

 

Une fois que vous avez obtenu l’emplacement votre adresse, il ne vous reste plus qu’à créer la punaise qui vous permettra de la situer sur le contrôle Bing Maps.

 

/// <summary>
/// Permet de créer une punaise et de l'associer au contrôle Bing Maps
/// </summary>
/// <param name="map">Le contrôle Bing Maps</param>
/// <param name="location">La localisation de l'adresse recherchée</param>
public void CreatePushpin(Map map, Location location)
{
	// Création de la punaise
	Pushpin pushpin = new Pushpin()
	{
		Name = "Adresse recherchée",
		Location = location
	};
	
	// Association au contrôle Bing Maps
	map.Children.Add(pushpin);
}

 

Voici le résultat pour une recherche sur l’adresse « Rue Auber, Paris » :

 

NB : Sur la carte ci-dessus, vous avez la punaise par défaut du contrôle. Celle-ci est une image, vous pouvez donc créer vos propres punaises.

 

2/. Bing Maps Spatial Data Services

Maintenant que vous avez géolocalisé votre adresse, vous avez la possibilité de rechercher des points d’intérêts (POI) autour de votre adresse en précisant le ou les types que vous souhaitez rechercher. (Voir le fichier joint se trouvant en bas de page "BingMaps (Liste POIEntityType).cs" : ce fichier contient une énumération des types de POI existants et le lien vers le MSDN sur la présentation de cette liste).

L’exemple de code ci-dessous vous présente une méthode vous permettant d’obtenir une liste de POI autour d’une localisation.

 

/// <summary>
/// Permet de trouver les points d'intérets autour d'une localisation (Ressource : http://msdn.microsoft.com/en-us/library/gg585131)
/// </summary>
/// <param name="location">Une localisation permettant de servir de point de recherche</param>
/// <param name="poiEntityTypeIds">Liste des POI EntityTypeId recherchés (entier séparé par une virgule)</param>
/// <param name="nbPoiSearch">Nombre de POI max à retourner</param>
/// <param name="radiusSearch">Radius de recherche (en km)</param>
/// <returns></returns>
public static List<Tuple<Location, string, int>> GetPOIs(Location location, string poiEntityTypeIds, int nbPoiSearch, int radiusSearch)
{
	List<Tuple<Location, string, int>> retour = default(List<Tuple<Location, string, int>>);
	
	// Création de la requête Bing Spatial Data Services 
	// Ressource : http://msdn.microsoft.com/en-us/library/hh478189
	string findNearbyPOIRequest = string.Format("http://spatial.virtualearth.net/REST/v1/data/c2ae584bbccc4916a0acf75d1e6947b4/NavteqEU/NavteqPOIs?spatialfilter=nearby({0},{1},{2})&$filter=EntityTypeID in({3})&$select=EntityID,DisplayName,__Distance,Latitude,Longitude,AddressLine,Locality,AdminDistrict,PostalCode,EntityTypeID&$top={4}&key={5}",
		location.Latitude, location.Longitude, radiusSearch,
		poiEntityTypeIds,
		nbPoiSearch,
		YOU_BING_MAPS_KEY);

	XmlDocument xmlDocument = GetXmlResponse(findNearbyPOIRequest);

	if(xmlDocument.IsNotNull())
	{
		retour = ReadPoiXmlDocument(xmlDocument);
	}
	
	return retour;
}

/// <summary>
/// Permet de lire le résultat de la recherche des POI
/// </summary>
/// <param name="xmlDocument">Document XML correspondant à la réponse de la requête au service</param>
/// <returns></returns>
private static List<Tuple<Location, string, int>> ReadPoiXmlDocument(XmlDocument xmlDocument)
{
	List<Tuple<Location, string, int>> retour = default(List<Tuple<Location, string, int>>);

	XmlNamespaceManager xmlNamespaceManager = new XmlNamespaceManager(xmlDocument.NameTable);
	xmlNamespaceManager.AddNamespace("d", "http://schemas.microsoft.com/ado/2007/08/dataservices");
	xmlNamespaceManager.AddNamespace("m", "http://schemas.microsoft.com/ado/2007/08/dataservices/metadata");
	xmlNamespaceManager.AddNamespace("a", "http://www.w3.org/2005/Atom");

	// Obtient les entityID pour chaque POI 
	// Ressource : http://msdn.microsoft.com/en-us/library/hh478191.aspx
	XmlNodeList displayNameList = xmlDocument.SelectNodes("//d:DisplayName", xmlNamespaceManager);

	if (displayNameList.Count > 0)
	{
		retour = new List<Tuple<Location, string, int>>();

		XmlNodeList addressLineList = xmlDocument.SelectNodes("//d:AddressLine", xmlNamespaceManager);
		XmlNodeList localityList = xmlDocument.SelectNodes("//d:Locality", xmlNamespaceManager);
		XmlNodeList adminDistrictList = xmlDocument.SelectNodes("//d:AdminDistrict", xmlNamespaceManager);
		XmlNodeList postalCodeList = xmlDocument.SelectNodes("//d:PostalCode", xmlNamespaceManager);
		XmlNodeList latitudeList = xmlDocument.SelectNodes("//d:Latitude", xmlNamespaceManager);
		XmlNodeList longitudeList = xmlDocument.SelectNodes("//d:Longitude", xmlNamespaceManager);
		XmlNodeList distanceList = xmlDocument.SelectNodes("//d:__Distance", xmlNamespaceManager);
		XmlNodeList entityTypeIdList = xmlDocument.SelectNodes("//d:EntityTypeID", xmlNamespaceManager);

		for (int i = 0; i < displayNameList.Count; i++)
		{
			Location locationPoi = new Location(Convert.ToDouble(latitudeList[i].InnerText), Convert.ToDouble(longitudeList[i].InnerText));

			int entityTypeId = Convert.ToInt32(entityTypeIdList[i].InnerText);

			retour.Add(new Tuple<Location, string, int>(locationPoi,
				string.Format(BingMapsResource.Label_PushpinTooltip,
						displayNameList[i].InnerText, 
						Environment.NewLine,
						addressLineList[i].InnerText,
						postalCodeList[i].InnerText,
						localityList[i].InnerText,
						adminDistrictList[i].InnerText,
						Convert.ToDouble(distanceList[i].InnerText), entityTypeId));
		}
	}

	return retour;
}

 

Comme vous pouvez le voir dans le code ci-dessus, l’URL de la requête contient plusieurs critères permettant de définir le rayon de la recherche, le ou les types de POI recherchés, le nombre maximum de POI à retourner ou bien encore les informations sur les POI à retourner.

NB : Pensez à bien définir vos critères de recherche car plus vous demanderez d’information, plus le fichier XML contenant la réponse sera volumineux et par conséquent vos temps de traitement seront plus long.

Voici le résultat pour une recherche de POI autour de l’adresse « Rue Auber, Paris » et pour les types de POI correspondant à une gare ou un transport ferroviaire :

 

Comme vous l’aurez remarqué, si vous ne créez pas vos propres punaises, on n’arrive plus à distinguer l’adresse recherchée et les POI. Je vous recommande donc vivement de créer vos propres punaises pour une meilleure utilisation du contrôle.

 

 Lien utile :

 

Fichier joint :

BingMaps (Liste POIEntityType).cs (14,15 kb)

Contrôle WPF Bing Maps (1/2) : Présentation de l’intégration du contrôle dans votre application WPF

by COlivier 11. octobre 2012 23:52

Dans un premier temps, je vais vous présenter comment intégrer le contrôle Bing Maps dans votre application WPF.

Dans un second billet, je vous présenterai l’utilisation du Bing Maps REST Services permettant de géolocaliser une adresse et l’utilisation du Bing Maps Spatial Data Services permettant de rechercher des POI (points d’intérêts) autour d’une adresse.

Après avoir téléchargé le contrôle et ajouté la référence dans votre projet, vous devrez déclarer un namespace dans votre fenêtre et ajouter votre contrôle :

A l’exécution, vous obtiendrez cet écran :

 

Comme vous l’aurez remarqué, un message vous indique que vos droits sont invalides. Ce message ne vous empêche pas de travailler avec le contrôle. Vous pouvez créer et tester vos menus de navigation et d’affichage, menus qui pourront vous permettre de zoomer sur la carte, centrer la carte à partir d’une position ou bien modifier le type d’affichage pour passer d’une vue plan à une vue satellite par exemple.  

Mais, bien entendu, lors de la livraison de votre application vous devrez enlever ce message.

Pour cela, vous devez obtenir une clé d’authentification Bing Maps à partir du portail Bing Maps (Compte Windows Live ID pour la connexion).

NB : Ce portail contient aussi différentes ressources autour du contrôle Bing Maps afin de vous informer et de vous aider sur la compréhension et l’utilisation du contrôle ainsi qu’une présentation des services qui l’entourent.

Une fois que vous aurez créé votre clé d’authentification, vous n’aurez plus qu’à la copier pour la déclarer dans votre contrôle avec l’attribut CredentialsProvider :

Et voilà, vous avez créé une Bing Maps dans votre application WPF.

 

Liens utiles : 


<!--[if !supportLineBreakNewLine]-->
<!--[endif]-->

Tags: , , , ,

Développement

Utilisation du Facebook SDK C# sur une application WPF

by COlivier 8. octobre 2012 20:00

Dans cet article, je vais vous présenter mon retour d’expérience sur l’utilisation de Facebook SDK C# que j’ai pu avoir sur ma dernière mission.

Tout d’abord, si vous effectuez une recherche sur l’utilisation du Facebook SDK C#, vous avez de fortes chances de tomber sur le blog de Nathan Tottent (un évangéliste technique de chez Microsoft) qui commencera par vous dire « Développer avec Facebook, c’est comme construire une maison sur un marécage ». Rien de très encourageant, surtout quand on vous demande de créer un module de publication de biens sur Facebook sur le compte d’utilisateur.

Avant de commencer à coder votre module, vous devrez tout d’abord télécharger le SDK Facebook C#, vous renseigner sur son fonctionnement et surtout créer une application Facebook. Car, c’est par l’intermédiaire de cette application Facebook que votre application WPF va pouvoir publier des informations sur les murs.

Comme pour toute application Facebook, les « Facebookiens » devront autoriser l’accès de votre application Facebook à leur compte, ce qui vous permettra d’obtenir un AccessToken.

Cet AccessToken contiendra des permissions comme l’accès aux informations personnelles, la permission de lire la liste des amis, de publier en son nom, etc. tout dépendra de ce que vous allez définir dans votre application Facebook.

NB : tout en sachant qu’un utilisateur peut bloquer certaines permissions de votre application à n’importe quel moment après avoir autorisé l’accès de votre application.

Voici donc l’exemple d’une méthode qui permet de publier du contenu sur une page Facebook :

 

using System;
using Facebook;

public class FacebookService
{
    /// <summary>
    /// Permet de poster sur Facebook les informations contenu dans un FacebookModel 
    /// </summary>
    /// <param name="facebookModel">Information de la publication</param>
    public void SendToFacebook(FacebookModel facebookModel)
    {
        try
        {
            // Token de permission de publication sur un compte utilisateur
            // ayant accepté l'accès à votre publication.
            string facebookAccessToken = AccessToken;
 
            // L'identifiant de votre application Facebook
            string facebookAppId = AppId;

            // Création du post Facebook
            dynamic parameters = new ExpandoObject();
            parameters.message = facebookModel.Message;
            parameters.link = facebookModel.Link;
            parameters.name = facebookModel.Name;
            parameters.caption = facebookModel.Caption;
            parameters.description = facebookModel.Description;
            parameters.picture = facebookModel.uriPicture;
            parameters.actions = new
                {
                    name = facebookModel.ActionName,
                    link = facebookModel.ActionUri,
                };
        
            FacebookClient facebookClient = new FacebookClient(facebookAccessToken);

            // Dans ce cas d'un paramétrage sur une page perso l'AccessToken est composé du [AppId|AppSecret]
            // Dans le cas d'une page pro on utilise un token pour se connecter au compte Facebook propriétaire de l'application précédement créée,
            // puis récupérer un AccessToken pour la page Pro, ce qui implique que le compte propriétaire doit être Gestionnaire de la page Pro
            if (facebookAccessToken.IndexOf('|').Equals(-1))
            {
                // Obtention de la liste des compte associés au compte propriétaire
                dynamic fbAccounts = facebookClient.Get("/me/accounts");

                // Boucle sur les comptes à la recherche de l'ID qui correspond à votre ID de destination (Fan Page ID)
                foreach (dynamic account in fbAccounts.data)
                {
                    if (account.id.Equals(facebookAppId))
                    {
                        parameters.access_token = account.access_token;
                        break;
                    }
                }
            }

            // Envoi de la requête de publication sur Facebook
            dynamic result = facebookClient.Post(string.Format("/{0}/feed", facebookAppId), parameters);

        }
        catch (FacebookApiException facebookApiException)
        {
            // Permet d'intercepter une exception sur la publication Facebook
        }
        catch (Exception exception)
        {
            // Permet d'intercepter une exception sur le traitement
        }
    }
}

 

 

Et voici un exemple de résultat d’une publication :

Liens utiles :

Tags: , ,

Développement

Problèmes courants lors de la configuration du webpart « Operations Manager Dashboard Viewer »

by Guyot 13. juin 2012 15:14

Si l’installation du webpart qui permet l’affichage de tableaux de bord SCOM 2012 dans SharePoint est plutôt bien documentée (voir notamment l’article http://technet.microsoft.com/en-us/library/hh212924.aspx) et ne présente pas de difficulté majeure, sa configuration peut se heurter à quelques subtilités.

Je vous livre ci-dessous quelques retours d’expérience en espérant vous faire ainsi économiser des instants biens précieux.

  1. Lorsque je tente de configurer le webpart « Operation Manager Dashboard Viewer », SharePoint m’affiche une page « Accès refusé»

clip_image002

Et pan, accès refusé !

clip_image004

Cause :

Le panneau de configuration du webpart interroge une liste hébergée par le site de l’administration centrale en utilisant pour cela l’identité du pool d’application (du site où vous avez posé le webpart). Le compte du pool d’application doit déjà avoir le droit d’accéder à cette liste.

Résolution :

Il suffit d’octroyer des permissions de lecture au compte du pool d’application dans l’administration centrale, soit sur la collection de site (cas illustré ci-dessous), soit sur la liste en elle-même en cassant l’héritage.

clip_image006

Après cela, vous partez confiant, le sourire aux lèvres, rafraîchir votre page en vous disant intérieurement : « ça y est, ça va marcher, mon chef va être content et je vais pouvoir enfin rentrer chez moi rejoindre ceux que j’aime (mes pizzas et mes bières quoi !)… »

Eh ben non, perdu ! …

2. Le compte du pool d’application a suffisamment d’autorisations sur l’administration centrale mais lorsque je tente de configurer le webpart « Operation Manager Dashboard View », j’ai un autre message d’erreur (code 0x080131904)

clip_image008

Le même message d’erreur dans sa version « développeur » :

clip_image010

Cause :

Donner les droits de lecture au compte du pool d’application sur l’administration centrale, pas plus que le mettre administrateur de la collection de sites, ne s’avère suffisant. Le compte doit aussi avoir le droit d’exécuter une procédure stockée de la base de contenu de l’administration centrale.

Résolution :

Il faut donner à ce même compte du pool d’application, le droit d’exécution sur la procédure stockée « proc_EnumLists » dans la base de données de contenu de l’administration centrale (« SharePoint_AdminContent_... »).

image

Et là, enfin, Ô joie, vous pouvez enfin configurer votre webpart pour afficher votre dashboard SCOM (ceci dit, ça ne vous aura pas dispensé de configurer la liste de l’administration centrale conformément à la procédure du Technet mentionnée précédemment).

A moins que …

3. Tout à l’air correctement configuré, dans l’Administration Centrale et dans le webpart mais en lieu et place de mon tableau de bord SCOM, rien ne s’affiche à part un bel espace tout blanc. Le dashboard ne s’affiche pas et il n’y a pas de message d’erreur.

 

Cause :

Il doit y avoir bien d’autre raison, mais je vous livre ici celle que j’ai rencontrée :

La console web SCOM est publiée en https et l’url utilisée par le webpart ne correspond pas au certificat SSL déclaré.

Par exemple : le certificat SSL est valide pour les url https://monserveur.mondomain.local mais le webpart essaye d’afficher un dashboar à l’url https://monserveur/operationsmanager/#/dashboard(type=Microsoft...)

C’est ballot n’est-ce pas … !

Résolution :

Dans la liste « Operations Manager Web Console Environments » hébergée dans l’administration centrale, éditer l’item de configuration utilisé par le webpart et corriger le champ « HostURI » pour qu’il corresponde au domaine du certificat.

clip_image014

Bon à savoir et autres petites contrariétés…

  • Par défaut, le Webpart « Operations Manager Dashboard Viewer » n’utilise pas le compte identifié dans SharePoint mais bien le compte de la session Windows. C’est pourquoi le dashboard peut afficher « Accès refusé » même si vous avez fait « Se connecter avec un utilisateur différent… » dans SharePoint et que le compte affiché par SharePoint 2010 a bien les permissions dans la console Operations Manager. Une alternative à ce problème est d’utiliser le « Secure Store » de SharePoint.

 

  • Le Webpart « Operations Manager Dashboard Viewer » utilize l’url configure telle que, la resolution de celle-ci est donc faite sur le poste client. Assurer vous que les utilisateurs, dans un contexte extranet par exemple, peuvent bien résoudre l’adresse du site SharePoint ET l’adresse de la console web d’Operations Manager.

Tags:

Collaboratif | Infrastructure | SharePoint

Le modèle MVVM selon KnockOut

by NRION 10. mai 2012 18:40

1 Introduction

1.1 Contexte

 

Depuis l’apparition de WPF et de Silverlight, un nouveau modèle d’architecture est apparu, poussé par les nouvelles fonctionnalités de liaison de données propres au langage XAML : MVVM (Model – View - ViewModel).
 
Dans le principe, ce modèle accentue l’idée de séparation des métiers de designer et de développeur. Les deux acteurs ne travaillant plus sur les mêmes fichiers, ils peuvent réaliser leurs travaux indépendamment l’un de l’autre. Je ne reviendrai pas sur les avantages et inconvénients de ce modèle qui a été étudié et décortiqué dans de nombreux articles sur la toile.
 
Après avoir retourné le modèle dans tous les sens, en le mettant en place dans quelques projets Silverlight, avec ou sans Framework (Jounce, Prism, MVVMLight, …), je me suis dit qu’il serait intéressant de s’ouvrir à d’autres technologies, à des langages qui m’ont toujours rebuté : Javascript et HTML.
 
J’ai donc étudié l’implémentation du modèle à travers un Framework Javascript nommé KnockOut.
 

1.2 Contraintes


Au début, ce qui m’a motivé à m’intéresser au modèle à travers les technologies C# et XAML, c’est avant tout la compatibilité entre deux technologies et surtout l’assemblage de langages fortement typés. Autrement dit, ce que l’on ne retrouve pas entre Javascript et C#.
Comme je n’avais pas vraiment envie de développer moi-même toutes les classes permettant de mettre en place le Binding Html avec Javascript (fonctionnalité qui n’existe pas de base), j’ai décidé d’étudier le Framework KnockOut dont j’ai eu quelques retours intéressants.
 

1.3 Mise en place


Etant en train d’étudier le Framwork Asp.Net MVC, il m’a semblé intéressant de mettre en place le modèle dans une architecture MVC avec Asp.net MVC3.
L’architecture est donc définie comme suit:


2 Un simple CRUD

2.1 Quelques explications


 Il me semble important de rappeler quelques informations importantes concernant le modèle MVVM. En effet, avec les technologies Silverlight et WPF, le modèle est mis en place, en général, entre une deux classes rassemblées au même endroit : l’interface (par exemple, un UserControl constitué d’une classe XAML et d’une classe C#) et son ViewModel (une simple classe C#).
 Or ici, nous avons une problématique importante : où met-on le code métier (du ViewModel) ?
 Si l’on se réfère au modèle, ce code doit être situé au même endroit que la vue, c’est-à-dire, sur le client !
 Cela signifie que l’on doit manipuler les objets Entity Framework (du Framework .Net) avec Javascript !
 Ci-dessous le parcours de la donnée de la base de données jusqu’à son affichage dans le formulaire HTML:
 
 
Dans un sens, on s’aperçoit que le nombre de mapping est important rien que pour lier des données entre une vue et un modèle de vue (un peu trop ?). Cela dit, la mapping entre la donnée SQL et l’objet .Net est réalisé par l’Entity Framework lui-même, ne nécessitant que très peu de manipulations de la part du développeur. On peut dire de même pour la transformation en JSON.
Non, le véritable problème se trouve au niveau de la récupération au « format » Javascript de KnockOut. Ceci est dû au fait que les objets javascript du Framework Knockout doivent être « observables », c’est-à-dire : Disposer de fonctionnalités leur permettant d’identifier les changements opérés sur leurs propriétés.
 

 2.2 Le code serveur 


Pour cet article, j’ai uniquement réalisé un POST et un GET pour illustrer la transition entre les objets Knockout et les objets .NET. Par ailleurs, je fais ici abstraction du code permettant de récupérer les données de la base.

Voici donc le code C# de la méthode de récupération des produits GetAllProducts du contrôleur :

[HttpGet]
public JsonResult GetAllProducts()
{
      List<Products> lst = NorthwindModel.GetProducts().Take(10).ToList();
      JsonResult js = Json(lst);
      js.JsonRequestBehavior = JsonRequestBehavior.AllowGet;
      return js;
}

 

Dans cette action, nous récupérons la liste des produits (les 10 premiers) qui sont des objets Entity. Nous convertissons ensuite la liste d’objets en JSON pour la retourner au client (Javascript).

A noter que nous devons permettre à notre objet JSON d’être transféré à travers une requête HTTP GET.

Ci-dessous la méthode C# permettant d’ajouter ou mettre à jour un Produit:
[HttpPost]
public JsonResult AddOrUpdate(Products p)
{
      NorthwindModel.AddOrUpdateProduct(p);
      var message = "Product " + p.ProductName + " Saved";
      JsonResult js = Json(message);

      return js;
}
 

Remarquez que le produit est passé en paramètre de la méthode et est reconnu comme un objet .Net alors qu’il est fourni par le client (toujours Javascript).

Le reste de la méthode est suffisamment simple pour ne pas nécessiter d’explications supplémentaires, simplement, un message est renvoyé au client sous forme JSON.

Les deux méthodes sont assez simples, le code de récupérations des données également. Il est aussi important de noter que les attributs de méthode HttpGet et HttpPost sont obligatoires pour le bon fonctionnement des appels.

 

 2.3 Le code javascript


Le code Javascript, écrit du côté client, va récupérer le message JSON envoyé par Asp.net et le transformer en un objet « observable » pour Knockout.

function initializeProducts() {
    $.getJSON("/Home/GetAllProducts", function (Datas) {
        var mappedProducts = $.map(Datas, function (item) {
            return new Product(item);
        })
        viewModel.products(mappedProducts);
    });
}

 

function Product(data) {
    this.ProductID = ko.observable(data.ProductID);
    this.ProductName = ko.observable(data.ProductName);
}

L’appel de la méthode initializeProducts() se fait dans le modèle de vue Produit comme ci-dessous.

var base = this;
var viewModel;

function ProductsViewModel() {
    // Data
    viewModel = this;

    base.initializeProducts();

Le code est exécuté lors du chargement de la page, ainsi, lorsque la page HTML est chargée dans le navigateur, le code Javascript qu’elle contient est exécuté par l’appel de la méthode applyBindings() du Framework Knockout.

ko.applyBindings(new ProductsViewModel());

 

Voici le récapitulatif du fonctionnement du modèle de vue :

La liste des produits est un tableau Javascript « observable :

viewModel.products = ko.observableArray([]);

 

Il ne nous reste plus qu’à afficher les données au « format » HTML.

L’ajout d’un produit se fait en deux étapes. D’abord, lors d’un clic sur le bouton ajouter, un nouveau produit est créé en Javascript :

this.newProduct = function () {
    viewModel.newProduct = ko.observable();
    viewModel.selectedProduct(new Product({ 'ProductID': 0, 'ProductName': '' }));
}

En termes de productivité, nous sommes assez loin de ce que peut nous apporter l’association de deux langages comme le XAML et le C#. Mais cela peut être dû au temps nécessaire pour s’habituer à cette syntaxe particulière et s’éloigner des technologies .Net et de leurs outils associés.

 

Il est clair qu’il y a certaines fonctionnalités supplémentaires à apporter si on veut pouvoir utiliser ce Framework dans un réel projet de grande envergure. Peut-être une implémentation dans les templates Visual Studio avec une simplification des communications entre les méthodes serveur et le code client ainsi qu’une amélioration dans le mapping entre les objets .Net et les objets Javascript.

 

Je n’ai étudié qu’une petite partie du Framework car celui-ci est tout de même assez conséquent, je le recommande à ceux qui souhaitent se lancer dans l’aventure M-V-VM tout en restant dans les environnements de type HTML/Javascript.

 

Je finirai tout de même en félicitant l’équipe de Knockout qui a parfaitement su mettre en place le modèle M-V-VM qui rajeunit deux langages qui peuvent paraître, à première vue, totalement contre-productif.

 

A suivre de près donc…

Tags: , , , ,

Développement

Intellisense avec JQuery

by Batiste Rouelle 24. février 2012 00:53

Si comme moi il vous arrive de travailler avec JQuery, il est tout de même pratique de disposer de l'intellisense. Avant on pouvait l'ajouter à la main dans Visual Studio, mais maintenant je vous conseille vivement d'installer NuGet. Il existe un package qui permet d'installer automatiquement l'intellisense pour JQuery. 

Tags:

Collaboratif | Développement | SharePoint

SSRS 2008 : Générer des rapports Reporting Services multi-langues (localisation)

by ygremillon 15. février 2012 20:13
Actuellement, le multi-langues dans un rapport Reporting Services peut être géré de plusieurs façons :
  • Créer un modèle de rapport par langue. C’est la méthode la plus simple, mais elle peut devenir très contraignante s’il y a un nombre importants de rapports / langues à traiter.
  • Gérer les libellés dans la base de données.
  • Utiliser la Globalisation / Localisation du Framework .NET via une librairie de ressources.
Nous allons nous intéresser à cette dernière méthode.
En premier lieu, nous allons créer la librairie de ressources.
Dans Visual Studio, nous créons un nouveau projet de librairie de classes.
On ajoute nos fichiers de ressources au projet. Pour mon exemple, le fichier de ressources se nomme : Localization.resx
On ajoute ensuite une nouvelle classe au projet, contenant le code ci-dessous :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Globalization;

    public static class LocalizedValue
    {
        public static string GetString(string cultureInfo, string resource, params object[] args)
        {
            CultureInfo ci = new CultureInfo(cultureInfo);
            string s = Localization.ResourceManager.GetString(resource, ci);
            if (args != null && args.Count() > 0)
                return string.Format(ci, s, args);
            else
                return s;
        }
    }
Une fois le projet compilé, il est nécessaire de copier l’ensemble des DLLs générées (le fichier de base et les fichiers de langues) sur le serveur SSRS.
Pour le designer (Visual Studio), les fichiers doivent être copiés dans le répertoire :
C:\Program Files\Microsoft Visual Studio 9.0\Common7\IDE\PublicAssemblies
Pour le serveur de reporting, les fichiers doivent être copiés dans le répertoire :
C:\Program Files\Microsoft SQL Server\MSRS.10.MonServeur\Reporting Services\ReportServer\bin
Coté rapport, notre DLL doit être référencée pour pouvoir être utilisée.
Pour cela, dans le menu « Report », on sélectionne « Report Properties » puis l’onglet « References ». Enfin, on ajoute la DLL de ressources.
Ensuite, il suffit d’ajouter l’expression suivante dans une TextBox. 
=MyLibrary.LocalizedValue.GetString(User!Language, "MaRessource")
MyLibrary est l’espace de noms définit par ma DDL de ressources (par défaut, le nom de la DLL).
LocalizedValue est le nom de la classe statique que l’on souhaite utiliser.
GetString est le nom de la méthode que l’on a défini précédemment.
User!Language est une variable prédéfinie par SSRS. Il s’agit d’une chaine de caractères contenant le code de la culture (fr-FR, en-US, …)
MaRessource est la clé de ma ressource
 

Tags: ,

Développement

Neos-SDI  Neos-SDI