Project Server 2013: edit Project Custom fields from Project Center (English Version)

by sgross 23. juin 2014 20:24

This article explains how to simplify Project Custom Field Update, directly from Project Center (in place edition). With this method, only 2 clics are necessary to update a KPI. With the standard method, more than 1 minute would be necessary. Source code is provided, and available for your specific needs.

This post is a translation from the original post written in French, further to some readers feedback.

In case of question or comment, you can contact me on twitter: @SylvainGrossNeo !

The code has been inspired by the Project 2013 SDK, and a postfrom @MartinLaukkanen.

Project Indicators

To improve the communication on projects, we often use Project Custom Fields: KPI’s like Project Health, Project Schedule, Project Cost, etc…These visual KPI’s will be readable in Project Center, were the whole portfolio is displayied, with the main figures: start/finish date, % achievement, etc…

Diffferent views can be defined in the Project Center, letting you the possibility to highlight relevant information, for exemple KPIs.

A Project Center displaying a lot of projects with red KPIs will require some attention from the PMO’s and executing directors !

image_thumb2

 

Updating Indicators

To update these KPIs, project leader have different possibilities:

  • A Project Detail Page: a web form available from PWA, where you can update Custom Fields
  • From Project Professional, he cans edit the Information of the project, including Custom Fields

image_thumb6

Those who are used to work with indicators will certainly confirm that those process a bit long: go to the project center, edit the project, select the PDP, change the value, save, archive…are long operations. If the Project Leader has to update many projects, he will waste a lot of time.

On viewing perspective, the above screenshot confirm that the graphical rendering is very basic: we can choose betweenn the words Red, Green, Yellow…It’s only in the Project Center that the indicator will be displayed with its graphical attributes.

In order to help one of my customer, I imagined and designed a different way to update the KPI’s: make the Project Center editable. The screenshoot and the video below shows briefly how it works. with a Project Health indicator.

image_thumb9

Small video (direct link here for fullscreen):

Changer le Project Health directement depuis Project Center

 

A writtable Project Center ?

The idea could seem to be stange: make the Project Center editable. But after a thinking time, we notice that the project center is just a web page…with some specificities!

To make the Project Center “writtable”, we “just” have to:

  • Know how to react on a cell click
  • Display some choices depending on the content to edit: smileys, text…
  • Know how to manage the user selection
  • Know how to update the project, with this new value
  • Nice to have: make it work in an instance of Project Online, not only On Premises. 

Clic on a cell in the Project Center

To manage the clic on the Project Center, we have to connect to the JSGrid of Project Center. After that, we have to attach to the Click event, to display the content, and propose the 3 choices (colors).

In the Project Server 2010 SDK, a starter kit proposed to customize the Project Center. This kit has not been updated for 2013, because the SDK is more focused on the new features like JSOM/CSOM (OM means Object Model).

Choose the cell, and clic on the indicator

In case of clic, we will simply have to replace the InnerHTML of the cell with ours: a list of IMG tags, with javascript eventhandlers.

Update the Project

In JSOM, the JS means Javascript: it is now possible to modify projects, resources, etc… in Javascript, on client side !

The second benefit is of course to allow Project Online customisation: because in this case we cannot access to WCF and PSI.

In CSOM or JSOM, the main challenges are:

  • asynchronous programming model, callbacks…
  • poor documentation

To update the project, we will need to:

  • Kwow which value has been selected
  • Get the value in the look up
  • Transform the value into its internal name
  • Publish the modified

The code

Disclaimer: the provided code is not ready for production. It’s more a explanation of the concept, and need to be developed, improved, industrialized…before putting in production..

The basics

All this code is written in Javascript, and is stored in a single js file.

To be integrated, different possibilities:

  • Create a SharePoint solution, including a feature.The layout folter will contain all the source code. I used this method because of the ease of debug. Follow this link the have more details: SDK here, and  this threadto start. 
  • Modify the Project Center, and integrate a Content Web Part, and paste the code inside.
  • Copy the JS in the Styles forder of the site, and reference the code is the project center. This method whould be used for Project Online instance.

OnClick

This following code is an adaptation of the PS2010 SDK.

http://msdn.microsoft.com/en-us/library/ff535984(v=office.14).aspx

/// <reference name="MicrosoftAjax.js"/>
/// <reference path="~/_layouts/inc/pwa/library/Utility.debug.js"/>
/// <reference path="~/_layouts/inc/pwa/library/WebMethodManager.debug.js"/>
/// <reference path="~/_layouts/inc/pwa/library/shell.debug.js"/>
/// <reference path="~/_layouts/inc/pwa/library/TimesheetSatellite.js"/>
/// <reference path="~/_layouts/inc/pwa/library/RemoteTextConv.debug.js"/>
/// <reference path="~/_layouts/inc/pwa/library/ProjectFramework.debug.js"/>
/// <reference path="~/_layouts/inc/pwa/library/GridSatellite.debug.js"/>
/// <reference path="~/_layouts/inc/pwa/library/projectserverscripts.debug.js"/>
/// <reference path="~/_layouts/inc/pwa/library/pagepropertymgr.debug.js"/>
/// <reference path="~/_layouts/SP.core.debug.js"/>
/// <reference path="~/_layouts/JsGrid.debug.js"/>
/// <reference path="~/_layouts/JsGrid.Gantt.debug.js"/>
/// <reference path="~/_layouts/sp.runtime.debug.js"/>
/// <reference path="~/_layouts/sp.debug.js"/>
/// <reference path="~/_layouts/ps.debug.js"/>
var pc; // Contains the Project Center extension object.
var JsGridControlInstance;
var JsGridSatellite;

var projContext;
var customFields;
var projects;
var customFieldData = [];
var currentProjectUid;
var currentIndicatorValue;
var currentCell;
var fieldName;
var lookupEntries = [];

_spBodyOnLoadFunctionNames.push("ProjectCenterMain");

function ProjectCenterMain() {
    pc = new ProjectCenterExtension();
    fieldName = 'Project Health';
}

function ProjectCenterExtension() {
    if (typeof projectCenterComponent === 'undefined')
        return;
    JsGridSatellite = projectCenterComponent.get_GridSatellite();
    JsGridControlInstance = projectCenterComponent.get_GridSatellite().GetJsGridControlInstance();
    JsGridControlInstance.AttachEvent(SP.JsGrid.EventType.OnSingleCellClick, CellClicked);
}

Cell choice et clic management

The purpose is to replace the InnerHTML content, with a liste of IMG tags.

function CellClicked(eventArgs) {
    currentCell = eventArgs.eventInfo.target;
    currentProjectUid = eventArgs.recordKey;
    //alert('Salut monde !');
    eventArgs.eventInfo.target.innerHTML = '' +
        '<img onclick=\"SelectIndicator(\'' + currentProjectUid + '\',\'Red\');\" width=\"16\" height=\"16\" title=\"Red\" style=\"border: 0px currentColor; margin-right: auto; margin-left: auto; display: block; max-height: 16px; max-width: 16px;\" src=\"/_layouts/15/images/../inc/pwa/images/cf_2p.png\"/><BR/>' +
        '<img onclick=\"SelectIndicator(\'' + currentProjectUid + '\',\'Yellow\');\"width=\"16\" height=\"16\" title=\"Yellow\" style=\"border: 0px currentColor; margin-right: auto; margin-left: auto; display: block; max-height: 16px; max-width: 16px;\" src=\"/_layouts/15/images/../inc/pwa/images/cf_1p.png\"/><BR/>' +
        '<img onclick=\"SelectIndicator(\'' + currentProjectUid + '\',\'Green\');\"width=\"16\" height=\"16\" title=\"Green\" style=\"border: 0px currentColor; margin-right: auto; margin-left: auto; display: block; max-height: 16px; max-width: 16px;\" src=\"/_layouts/15/images/../inc/pwa/images/cf_0p.png\"/><BR/>';
}

 

Lookup loading

We keep in mind UID of Project, and the selected value. Now let’s load the custom fields. In case of lookup, the different states must be stored.

function SelectIndicator(projUID, color) {
    currentProjectUid = projUID;
    currentIndicatorValue = color;
    ProcessProject();
}

function ProcessProject() {
    projContext = PS.ProjectContext.get_current();
    customFields = projContext.get_customFields();
    projContext.load(customFields);
    
    projContext.executeQueryAsync(getCFComplete, getCFFailed);
}

function getCFFailed(sender, args) {
    alert('Boum');
}

function getCFComplete(response) {
    var cfEnumerator = customFields.getEnumerator();
    while (cfEnumerator.moveNext()) {
        var cf = cfEnumerator.get_current();
        // Ajout à la liste des customFields

        customFieldData.push({
            Id: cf.get_id(),
            Name: cf.get_name(),
            InternalName: cf.get_internalName()
        });
        // S’agit il de notre Custom Field, et s’agit il d’un lookup ?
        if (cf.get_name() === fieldName) {
            var lookupTable = cf.get_lookupTable();
            // Prépare au Chargement de la lookup de ce custom field
            projContext.load(lookupTable);
            // Le code qui suit va s’exécuter en asynchrone, à l’aide d’une fonction anonyme

            projContext.executeQueryAsync(function () {
                var ltEntries = lookupTable.get_entries();
                // Recherche les valeurs de la lookup
                projContext.load(ltEntries);
                projContext.executeQueryAsync(function () {
                    var ltEnum = ltEntries.getEnumerator();
                    while (ltEnum.moveNext()) {
                        var ltEntry = ltEnum.get_current();
                        // Stocke chaque élément de lookup dans le tableau, en incluant son InternalName
                        lookupEntries.push({
                            InternalName: ltEntry.get_internalName(),
                            Value: ltEntry.get_value(),
                            FullValue: ltEntry.get_fullValue(),
                            Description: ltEntry.get_description()
                        });
                    }
                }, getCFFailed);

                // Lorsque la lookup et le custom field sont chargés, on va charger les projets
                GetProjects();
            }, getCFFailed);
        }
    }

 

Load project, and Update

The project load works asynchronously. The UpdateProject method is called. The internal UID must be found for the different( lookup entries. The  setCustomFieldValue function waits for something like that::

PS.DraftProject.setCustomFieldValue(
   'Custom_x005f_abc8d78a1f5e211940b00155d000a03',
    ['Entry_abc76e70a1f5e211940b00155d000a03']);)

We need to do some work to make it work….…

function GetProjects() {
    projects = projContext.get_projects();
    projContext.load(projects, "Include( Name, CreatedDate, Id, IsCheckedOut )");
    projContext.executeQueryAsync(updateProject, getCFFailed);
} 

function updateProject() {
    var projectId = currentProjectUid;
    var project = projects.getById(projectId);
    var draftProject = project.checkOut();


    // Retrouver le Custom Field à l’aide de son nom
    var cfData = $.grep(customFieldData, function (val) {
        return val.Name === fieldName;
    });
    // Retrouver la valeur du Lookup choisi
    var leData = $.grep(lookupEntries, function (val) {
        return val.Value === currentIndicatorValue; //"Red"; 
    });
    // Retrouve l’Id et donc son InternalName
    if (leData.length > 0 && cfData.length > 0) {

        // La méthode setCustomFieldValue attend le lookup sous forme de Tableau, d’où la syntaxe un peu spéciale.
        var lookupInternalName = [leData[0].InternalName];
        draftProject.setCustomFieldValue(cfData[0].InternalName, lookupInternalName);
    }
    // Ce code permettrait de gérer le custom fields non lookup
    else if (cfData.length > 0) {
        draftProject.setCustomFieldValue(cfData[0].InternalName, "Ma valeur de CF");
    }
    //Publication
    var publishJob = draftProject.publish(true);
    //Attente de fin de traitement avec analyse du code de retour
    projContext.waitForQueueAsync(publishJob, 30, function (response) {
        if (response !== 4) {
            alert('Erreur dans la publication du projet:' + response + '. Pour une liste des codes, se rendre sur http://msdn.microsoft.com/en-us/library/office/microsoft.projectserver.client.jobstate_di_pj14mref(v=office.15)');
        } else {
            // En cas de publish correct, la page est rechargée (à optimiser par un refresh coté JS)

            location.reload(true);
        }
    });
}

Test and Debug

The easiest way is certainly to work with a “local” Project Server 2013 and use breakpoints in order to understand what is happening.

To work, the Project Center must reference some JS, through a Content Web part.

image_thumb21

In case of emergency: Twitt me @SylvainGrossNeo !

Tags:

Project Server 2013: éditer les champs personnalisés depuis le Centre de Projet

by sgross 13. juin 2014 10:14

Cet article est une petite étude montrant comment simplifier la mise à jour des champs projets, directement depuis le Centre de Projet (édition sur place). La méthode proposée permet une mise à jour d’une propriété en 2 clics, alors que la méthode standard nécessite au moins une minute…

Les codes sources sont bien entendu fournis, et librement adaptables à vos spécificités.

Pour toute question sur cet article ou sur Project Server 2013, merci de contacter @SylvainGrossNeo sur Twitter !

Le code s’inspire du SDK de Project Server 2013, ainsi que d’un postde @MartinLaukkanen.

Les indicateurs de projets

Pour faciliter la communication sur l’état de ses projets, il est habituel d’utiliser des champs personnalisés d’entreprise. Il s’agira par exemple de KPI, d’indicateurs visuels.

Le centre de projets de Project Server 2013 permet de représenter l’ensemble des projets de l’entreprise, en affichant des informations clés: date de début, date de fin, travail, % avancement, indicateurs.

Cette représentation très visuelle permet de rapidement identifier les projets qu’il faut surveiller de près: des indicateurs rouges ne sont pas de bons signes !

image

 

Mettre à jour les indicateurs

Pour mettre à jour ces champs personnalisés, le chef de projets a 2 possibilités:

  • Une page de détail de projet, ou PDP: c’est un formulaire Web accessible depuis PWA qui permet d’éditer les champs personnalisés
  • Il peut modifier ces champs depuis Project Professional, le client lourd de Project.

image

Ceux qui ont l’habitude de travailler avec les indicateurs confirmeront sans doute que le processus de mise à jour est un peu lourd: aller dans le centre de projet, ouvrir le projet, l’éditer, sélectionner le bon PDP, sélectionner la valeur pour le champ, sauver, archiver…sont des opérations relativement longues. De plus si le chef de projet a plusieurs projets à mener en parallèle, le temps de mettre à jour ses indicateurs n’est pas négligeable.

Sur la capture ci dessus, on se rend également compte que le rendu graphique est basique: le choix Red, Yellow et Green est textuel: ce n’est que dans le Centre de Projet que l’indicateur prendra son apparence graphique.

C’est pour essayer de simplifier le travail de mes clients que j’ai conçu un moyen différent de mettre à jour les indicateurs: rendre le Centre de Projet éditable. Voici un aperçu, dans lequel le champ Project Health, peut être mis à jour avec 3 couleurs reflétant la santé du projet.

image

Petit extrait vidéo (lien direct ici pour le plein écran):

Changer le Project Health directement depuis Project Center

 

Un Centre de Projet éditable ??

L’idée peut sembler un peu farfelue, mais en y réfléchissant un peu, on s’aperçoit que le Centre de Projet n’est somme toute qu’une page web, un peu spéciale certes !

Pour rendre le Centre de Projet éditable, il suffit de:

  • Savoir réagir au clic sur la cellule à mettre à jour depuis le centre de projet
  • Proposer les choix possibles pour cette cellule, en fonction du type de contenu: smiley, textes, indicateurs graphiques
  • Savoir réagir au clic sur l’élément sélectionné par l’utilisateur
  • Savoir mettre à jour le projet avec les nouvelles valeurs de champs
  • Pour couronner le tout, permettre une utilisation depuis Project Online serait un plus non négligeable

Clic sur une cellule du Centre de Projet

Pour réagir au clic sur le centre de projet, il faut s’interconnecter à la JSGrid de Project Center. Il s’agit ensuite de s’attacher à l’évènement Clic sur une cellule, pour modifier son contenu, et le remplacer par les 3 choix de couleurs de notre exemple.

Dans le SDK de Project Server 2010, il existait un Starter Kit permettant de customiser le Centre de Projet. Ce Starter kit n’a pas été mis à jour vers 2013, car le SDK se focalise essentiellement sur la nouveauté principale: le nouveau modèle de programmation CSOM et JSOM (OM = Object Model). Nous détaillerons ce point un peu plus loin.

Choix pour la cellule, et clic sur l’indicateur

Il suffira de remplacer le code InnerHTML de la cellule par notre code: des IMG avec des gestionnaires d’évènements.

Mettre à jour le projet

En programmation JSOM, le JS représente Javascript. Cela signifie que désormais il est possible de modifier des projets avec du code Javascript client !

Un modèle objet permet d’effectuer toute sorte d’opérations sur les projets, les champs personnalisés, les ressources, etc…Ce code s’exécute coté client, ce qui ouvre d’énormes possibilités.

L’autre intérêt est évidemment de pouvoir fonctionner Online: en effet dans une instance de Project dans le cloud, il est hors de question d’accéder aux services Web WCF et PSI.

Nous le verrons, travailler coté client avec Javascript permet de simplifier le code de manière impressionnante.

Toutefois, ce mode de fonctionnement exige des précautions: en Javascript, le code va s’exécuter en mode asynchrone. Des fonctions de Synchronisation et de Callback sont à mettre en place pour être sûr que les méthodes s’exécutent au moment voulu. C’est la principale difficulté.

Pour mettre à jour notre projet, nous aurons besoin de:

  • Savoir quelle valeur a été choisie par l’utilisateur
  • Retrouver cette valeur dans une liste de choix
  • Transformer cette valeur, en nom interne InternalName, qui est la syntaxe attendue par l’API Javascript. Je salue ici le remarquable travail de défrichage engagé par Martin Laukkanen sur son blog.
  • Publier le projet et rafraichir la page.

Le code

Attention, le code fournit ici n’est pas clé en main. C’est une illustration du concept, qu’il faut encore développer, améliorer, fiabiliser, industrialiser avec de déployer en production.

Le projet de base

L’ensemble du code est écrit en Javascript, et tient dans un fichier js.

Pour l’intégrer, il existe plusieurs moyens:

  • Créer une solution SharePoint, contenant une feature. Le dossier Layouts contiendra tous les sources. C’est la méthode que j’ai provisoirement adoptée, à cause des facilités de debug offerte. S’inspirer du SDK ici, et du threadsuivant  pour démarrer.
  • Modifier la page Project Center, en ajoutant une Webpart de Contenu, dans laquelle vous pouvez copier le code Javascript (voir la dernière partie de cet article, car même si la méthode SharePoint est utilisée, des scripts additionnels doivent être ajoutés).
  • Copier le code Javascript dans un répertoire Styles du site, et référencer ce code depuis la page Project Center. Cette méthode convient particulièrement pour les instances Online. Je mettrai ce post à jour dès que j’aurais effectué l’opération.

Réagir au clic

Il s’agira ici de se connecter à l’évènement Clic sur une cellule du Centre de Projet. Pour que ce code fonctionne, il faut que l’infrastructure de customisation du Project Center soit en place. Pour ceux qui auront du mal à intégrer ce code, il faudra attendre que je publie mon code dans la Gallery, ou me contacter via Twitter. En fonction des retours j’intègrerai des explications complémentaires, ou un nouveau post d’initiation aux extensions du Centre de Projet.

L’article du MSDN http://msdn.microsoft.com/en-us/library/ff535984(v=office.14).aspxfournit une introduction, mais des adaptations sont nécessaires à cause du changement de version vers 2013.

/// <reference name="MicrosoftAjax.js"/>
/// <reference path="~/_layouts/inc/pwa/library/Utility.debug.js"/>
/// <reference path="~/_layouts/inc/pwa/library/WebMethodManager.debug.js"/>
/// <reference path="~/_layouts/inc/pwa/library/shell.debug.js"/>
/// <reference path="~/_layouts/inc/pwa/library/TimesheetSatellite.js"/>
/// <reference path="~/_layouts/inc/pwa/library/RemoteTextConv.debug.js"/>
/// <reference path="~/_layouts/inc/pwa/library/ProjectFramework.debug.js"/>
/// <reference path="~/_layouts/inc/pwa/library/GridSatellite.debug.js"/>
/// <reference path="~/_layouts/inc/pwa/library/projectserverscripts.debug.js"/>
/// <reference path="~/_layouts/inc/pwa/library/pagepropertymgr.debug.js"/>
/// <reference path="~/_layouts/SP.core.debug.js"/>
/// <reference path="~/_layouts/JsGrid.debug.js"/>
/// <reference path="~/_layouts/JsGrid.Gantt.debug.js"/>
/// <reference path="~/_layouts/sp.runtime.debug.js"/>
/// <reference path="~/_layouts/sp.debug.js"/>
/// <reference path="~/_layouts/ps.debug.js"/>
var pc; // Contains the Project Center extension object.
var JsGridControlInstance;
var JsGridSatellite;

var projContext;
var customFields;
var projects;
var customFieldData = [];
var currentProjectUid;
var currentIndicatorValue;
var currentCell;
var fieldName;
var lookupEntries = [];

_spBodyOnLoadFunctionNames.push("ProjectCenterMain");

function ProjectCenterMain() {
    pc = new ProjectCenterExtension();
    fieldName = 'Project Health';
}

function ProjectCenterExtension() {
    if (typeof projectCenterComponent === 'undefined')
        return;
    JsGridSatellite = projectCenterComponent.get_GridSatellite();
    JsGridControlInstance = projectCenterComponent.get_GridSatellite().GetJsGridControlInstance();
    JsGridControlInstance.AttachEvent(SP.JsGrid.EventType.OnSingleCellClick, CellClicked);
}

Choix de cellule et gestion du clic sur l’indicateur

Il s’agit ici de remplacer le code innerHTML par celui qui nous convient: 3 images, et une gestion de l’évènement Clic en passant les bons paramètres.

Les noms de variables sont suffisamment explicite pour comprendre le sens…je l’espère !

L’URL des images est à adapter bien sur en fonctions des images que vous souhaiter voir afficher. Il n’y a pas de recherche dynamique de l’image en fonction du paramétrage du Custom Field.

function CellClicked(eventArgs) {
    currentCell = eventArgs.eventInfo.target;
    currentProjectUid = eventArgs.recordKey;
    //alert('Salut monde !');
    eventArgs.eventInfo.target.innerHTML = '' +
        '<img onclick=\"SelectIndicator(\'' + currentProjectUid + '\',\'Red\');\" width=\"16\" height=\"16\" title=\"Red\" style=\"border: 0px currentColor; margin-right: auto; margin-left: auto; display: block; max-height: 16px; max-width: 16px;\" src=\"/_layouts/15/images/../inc/pwa/images/cf_2p.png\"/><BR/>' +
        '<img onclick=\"SelectIndicator(\'' + currentProjectUid + '\',\'Yellow\');\"width=\"16\" height=\"16\" title=\"Yellow\" style=\"border: 0px currentColor; margin-right: auto; margin-left: auto; display: block; max-height: 16px; max-width: 16px;\" src=\"/_layouts/15/images/../inc/pwa/images/cf_1p.png\"/><BR/>' +
        '<img onclick=\"SelectIndicator(\'' + currentProjectUid + '\',\'Green\');\"width=\"16\" height=\"16\" title=\"Green\" style=\"border: 0px currentColor; margin-right: auto; margin-left: auto; display: block; max-height: 16px; max-width: 16px;\" src=\"/_layouts/15/images/../inc/pwa/images/cf_0p.png\"/><BR/>';
}

 

Chargement des lookup et du custom field

L’UID du projet et la valeur à positionner sont mémorisés. Les custom fields doivent à présent être chargés, et, dans le cas du custom field “Lookup" les différents états doivent être stockés.

function SelectIndicator(projUID, color) {
    currentProjectUid = projUID;
    currentIndicatorValue = color;
    ProcessProject();
}

function ProcessProject() {
    projContext = PS.ProjectContext.get_current();
    customFields = projContext.get_customFields();
    projContext.load(customFields);
    
    projContext.executeQueryAsync(getCFComplete, getCFFailed);
}

function getCFFailed(sender, args) {
    alert('Boum');
}

function getCFComplete(response) {
    var cfEnumerator = customFields.getEnumerator();
    while (cfEnumerator.moveNext()) {
        var cf = cfEnumerator.get_current();
        // Ajout à la liste des customFields

        customFieldData.push({
            Id: cf.get_id(),
            Name: cf.get_name(),
            InternalName: cf.get_internalName()
        });
        // S’agit il de notre Custom Field, et s’agit il d’un lookup ?
        if (cf.get_name() === fieldName) {
            var lookupTable = cf.get_lookupTable();
            // Prépare au Chargement de la lookup de ce custom field
            projContext.load(lookupTable);
            // Le code qui suit va s’exécuter en asynchrone, à l’aide d’une fonction anonyme

            projContext.executeQueryAsync(function () {
                var ltEntries = lookupTable.get_entries();
                // Recherche les valeurs de la lookup
                projContext.load(ltEntries);
                projContext.executeQueryAsync(function () {
                    var ltEnum = ltEntries.getEnumerator();
                    while (ltEnum.moveNext()) {
                        var ltEntry = ltEnum.get_current();
                        // Stocke chaque élément de lookup dans le tableau, en incluant son InternalName
                        lookupEntries.push({
                            InternalName: ltEntry.get_internalName(),
                            Value: ltEntry.get_value(),
                            FullValue: ltEntry.get_fullValue(),
                            Description: ltEntry.get_description()
                        });
                    }
                }, getCFFailed);

                // Lorsque la lookup et le custom field sont chargés, on va charger les projets
                GetProjects();
            }, getCFFailed);
        }
    }

 

Charger le projet et le mettre à jour

Le chargement du projet se fait en asynchrone: la méthode UpdateProject est appelée. Après extraction du projet, il s’agit de retrouver le UID internes des éléments de lookup, et du custom field. La fonction setCustomFieldValue attend quelque chose comme ça:

PS.DraftProject.setCustomFieldValue(
   'Custom_x005f_abc8d78a1f5e211940b00155d000a03',
    ['Entry_abc76e70a1f5e211940b00155d000a03']);)

Il y a par conséquent un peu de travail pour parvenir à ce résultat…

function GetProjects() {
    projects = projContext.get_projects();
    projContext.load(projects, "Include( Name, CreatedDate, Id, IsCheckedOut )");
    projContext.executeQueryAsync(updateProject, getCFFailed);
} 

function updateProject() {
    var projectId = currentProjectUid;
    var project = projects.getById(projectId);
    var draftProject = project.checkOut();


    // Retrouver le Custom Field à l’aide de son nom
    var cfData = $.grep(customFieldData, function (val) {
        return val.Name === fieldName;
    });
    // Retrouver la valeur du Lookup choisi
    var leData = $.grep(lookupEntries, function (val) {
        return val.Value === currentIndicatorValue; //"Red"; 
    });
    // Retrouve l’Id et donc son InternalName
    if (leData.length > 0 && cfData.length > 0) {

        // La méthode setCustomFieldValue attend le lookup sous forme de Tableau, d’où la syntaxe un peu spéciale.
        var lookupInternalName = [leData[0].InternalName];
        draftProject.setCustomFieldValue(cfData[0].InternalName, lookupInternalName);
    }
    // Ce code permettrait de gérer le custom fields non lookup
    else if (cfData.length > 0) {
        draftProject.setCustomFieldValue(cfData[0].InternalName, "Ma valeur de CF");
    }
    //Publication
    var publishJob = draftProject.publish(true);
    //Attente de fin de traitement avec analyse du code de retour
    projContext.waitForQueueAsync(publishJob, 30, function (response) {
        if (response !== 4) {
            alert('Erreur dans la publication du projet:' + response + '. Pour une liste des codes, se rendre sur http://msdn.microsoft.com/en-us/library/office/microsoft.projectserver.client.jobstate_di_pj14mref(v=office.15)');
        } else {
            // En cas de publish correct, la page est rechargée (à optimiser par un refresh coté JS)

            location.reload(true);
        }
    });
}

Test et déboguage

Le plus simple est sans doute de travailler avec un Project Server 2013 “local” au poste de développement, de manière à pouvoir déboguer avec des points d’arrêt, et bien comprendre ce qui se passe.

Pour fonctionner, la page Project Center doit référencer des scripts JS, comme indiqué dans la capture suivante:

image

Ce blog sera mis à jour lorsque le code sera mis en partage du Code Gallery, avec quelques instructions supplémentaires pour le déploiement.

En cas de difficulté, me contacter sur Twitter @SylvainGrossNeo.

Tags:

EPM | Développement | Innovation

Déploiement automatique d’une application Web/WCF à l’aide de Release Manager for Visual Studio 2013 – Part 1

by sgross 13. mars 2014 17:46

Introduction

Cet article est le premier des 2 billets consacrés à Release Management for Visual Studio 2013.

Il s’agit d’un complément à la session que j’ai animée aux Techdays 2014 avec Jean Christophe, dont le Webcast est disponible ici, et les slides ici.

Contrairement à la session des Techdays, cette présentation s’inspire d’un cas client réel que nous venons de réaliser. Bien entendu, le nom des machines, des comptes, des applications et des acteurs a été modifié: toute ressemblance serait donc fortuite.

Les bases de Release Manager ne sont pas décrites dans cet article: le lien suivant décrit de manière très simple et complète les possibilités du produit. Il s’agit dans cet article de présenter Release Manager for Visual Studio 2013 dans un cas d’utilisation réel.

La présentation est découpée en 2 parties:

  • Problématique à résoudre, de l’installation et de la mise en place des bases
  • Réalisation des outils et composants, puis implémentation du workflow de déploiement

Contexte

L’application qui fait l’objet de cet article est l’ERP du client. Il travaille dans la grande distribution, et possède une quarantaine de magasins, pour un CA de 180M€. Cet ERP est un développement spécifique permettant de gérer les produits, les fournisseurs, les achats, les ventes, les magasins…c’est donc une application réellement vitale !

Il s’agit d’une application Web basée sur les derniers framework techniques, et utilise une couche de service WCF. L’application est répartie sur 5 serveurs, avec un dispositif de Load Balancing, un cache AppFabric. La gestion des données y joue évidemment un rôle capital.

Pour ce projet, la problématique du déploiement s’est rapidement posée. Nous avons d’abord mis en œuvre un déploiement basé sur MSBuild et MSDeploy (pour des précisions sur la mise en œuvre de MSDeploy, voir l’excellente série d’articles de Charles Barjansky).

Cependant, les déploiements restaient fastidieux à réaliser: une build par serveur rendait le processus de déploiement un peu lourd.

De plus, il est important de sécuriser ce déploiement, en prévoyant les cas où un incident se produirait. Le retour en arrière automatisé en cas de problème de déploiement est le moyen le plus rapide de revenir à une situation stable.

Voici une vision simplifiée de l’architecture d’intégration sur laquelle nous avons travaillé.

image

A noter que sur cet environnement d’intégration, les serveurs Web et WCF sont doublés.

Le processus de déploiement

Déploiement standard

Le déploiement normal doit se passer de la manière suivante :

image

Déploiement « Retour Arrière »

En cas de problème pendant le déploiement, et surtout lors d’une validation rejetée, Release Manager est capable d’effectuer un retour en arrière.

image

Contraintes et limitations

Le déploiement doit pouvoir être effectué quel que soit l’état initial de la plateforme :

  • IIS démarré ou pas
  • Cache démarré ou pas
  • Fichiers de mise à jour de base présents ou pas

Les versions successives des applications sont conservées.

Le retour en arrière se base toujours sur la version précédemment déployée, aussi bien pour la base de données, que pour les applications.

Le déclenchement d’un retour arrière, se fait par l’appréciation d’un opérateur manuel, selon qu’il approuve ou pas ce déploiement. Il n’y a pas de test automatique pour le moment.

Installation et paramétrage de base

Vue d’ensemble

Après avoir installé tous les composants, les activités pour mener à bien cette automatisation correspondent aux étapes décrites dans la documentation de Release Manager.

image

Installation

La procédure d’installation est décrite en détail dans le Guide d’installation fourni avec le produit. Il peut être téléchargé ici, ainsi que les différents composants logiciels de Release Manager 2013.

L’architecture de Release Management est la suivante :

image

Le point important est l’installation de l’outil Client Release Manager sur le serveur de Build. En effet, lors d’une Build, des appels à Release Management Server sont effectués : les outils client doivent être présents sur le serveur de Build. L’omission de ce client occasionne des erreurs lors des builds Release Manager.

Mise en place des basiques

Configuration générale

Dans un premier temps, il est nécessaire de connecter l’outil client au serveur Release Manager.

image

Lien avec TFS

Le lien avec le serveur TFS peut être mis en place, en précisant le compte de service utilisé.

image

Liste de choix

Des listes de choix sont utilisées pour caractériser les serveurs (par technologies) ou les phases (nom de phases).

imageimage

 

 

 

 

 

 

 

 

Groupes et utilisateurs

Il est pratique de se baser sur la sécurité de TFS pour simplifier le travail de maintenance des groupes. Le groupe TFS “Administrateur de Build” peut être un choix pertinent pour l’administration des déploiements Release Manager. En fonction du contexte, il conviendra de créer un groupe spécifique.

image

Les utilisateurs présents dans Release Manager proviennent:

  • Des groupes précédemment déclarés
  • Des comptes de services utilisés. Les comptes de services sont marqués en tant que Utilisateur de service.

image

Les chemins de déploiement

Les serveurs

Les serveurs doivent tous exécuter l’Agent de déploiement. Il faut par conséquent l’installer sur chaque serveur :

  • IIS / Web
  • IIS / WCF
  • SQL Server

Le choix du compte d’exécution de ce service est fait à la fin de l’installation. Ce compte devient administrateur local du poste. Il s’ajoute également à la liste des utilisateurs / Utilisateur du service de Release Manager.

La procédure d’installation des Agents est décrite dans la documentation du produit. Lorsque les serveurs sont installés, il s’agit de les déclarer dans l’outil. Le plus rapide est de lancer une détection des serveurs présents.

image

Les environnements

Les environnements sont créés en ajoutant les différents serveurs concernés.

image

 

Le chemin d’accès à la version finale

Seul le déploiement en intégration est programmé. Le Test ou la Production sont traitées différemment pour le moment.

image

L’acceptation du déploiement est automatisée, de façon à pouvoir déclencher le déploiement depuis une Build TFS directement.

Résultat

Le résultat obtenu après la programmation de ce déploiement est présenté ci dessous.

Avant cela, il y a bien sûr un peu de travail à réaliser. Ces activités seront décrites dans la deuxième partie !

image

Tags: , , , , ,

ALM | Développement

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

by Charles BARJANSKY 31. janvier 2014 20:01

Préambule

Cet article complète la série des 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 qui détient le rôle IIS.
Pour rappel, les quatre parties sont :

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 3

L’objectif de cette troisième partie est de décrire et de mettre en œuvre le fournisseur de sauvegarde automatique présent dans la solution Microsoft Web Deploy nommé BackupManager.
Cette solution permet :

  • de sauvegarder automatiquement une application web ainsi que sa base de données associée lors de chaque publication effectuée à l’aide l’outil Web Deploy ;
  • de restaurer une sauvegarde sans l’intervention d’un administrateur lors d’un échec de la publication ou lors du souhait de l’utilisateur de restaurer une ancienne version de l’application web.

Description de la solution de sauvegarde automatique IIS

Lors de chaque déploiement d’une application web via le déploiement web (voir les précédents articles), le fournisseur IIS backupManager identifie les différents fournisseurs consommés et sauvegarde automatiquement le contenu géré par ces fournisseurs.

Par exemple, si les fournisseurs lors d’un déploiement sont iisApp (gestion des fichiers du site) et dbFullSql (publication de la structure complète du schéma et des données) alors tous les fichiers du site et la base de données sont sauvegardés.

Ainsi, si besoin, l’utilisateur peut restaurer cette version précédente de l’application web à l’aide de l’outil msdeploy.


Mise en œuvre de la solution

Pré-Requis

Les pré-requis sont identiques à ceux de l’article précèdent. Pour rappel :

Pour permettre le succès du déroulement des deux mises en œuvre étudiées (dbFullSql & dbDacFx), veuillez suivre les tâches suivantes :
  1. Suivre l’article "Mise en oeuvre de l’outil de déploiement web IIS (Microsoft Web Deploy 3 .0) – Partie 1" ;
  2. Installation du package “Microsoft® System CLR Types for Microsoft® SQL Server® 2012 SP1” (disponible à l’adresse suivante : http://www.microsoft.com/en-us/download/details.aspx?id=35580) sur le/les serveurs qui détiennent le rôle IIS ;
  3. Installation du package “Microsoft® SQL Server® 2012 SP1 Shared Management Objects” (disponible à l’adresse suivante : http://www.microsoft.com/en-us/download/details.aspx?id=35580) sur le/les serveurs qui détiennent le rôle IIS ;
  4. Obtention des paramétrages de connexion d’un utilisateur SQL ayant les droits de création de base de données (dbcreator) et de modification des informations de sécurité (securityadmin) sur une instance Microsoft SQL Server (différent du paramétrage de connexion existant de l'application web).

De plus, pour la mise en œuvre de la solution du fournisseur dbDacFx, veuillez effectuer les tâches complémentaires suivantes :

  1. Installation du package “Microsoft® SQL Server® 2012 SP1 Transact-SQL ScriptDom” (disponible à l’adresse suivante : http://www.microsoft.com/en-us/download/details.aspx?id=35580) sur le/les serveurs qui détiennent le rôle IIS ;
  2. Installation du package “Microsoft® SQL Server® 2012 Data-Tier Application Framework (March 2013)” (disponible à l’adresse suivante : http://www.microsoft.com/en-us/download/details.aspx?id=36842) sur le/les serveurs qui détiennent le rôle IIS.

Activation du fournisseur backupManager

L’intégralité des opérations s’effectue à l’aide d’un script PowerShell présent dans le dossier Program Files de l’outil « Microsoft Web Deploy v3 ».

  1. A l’aide d’une console Powershell sur le serveur qui héberge le rôle IIS, exécuter les commandes :
    Set-Location "$env:ProgramFiles\IIS\Microsoft Web Deploy V3\Scripts"
    . .\BackupScripts.ps1
    Remarques : veillez à bien ajouter le point devant « .\BackupScripts.ps1 ». Ce point permet l’inclusion des fonctions présentes dans le fichier dans la session Powershell.
     
  2. Activer le fournisseur BackupManager :
    TurnOn-Backups -On $true
  3. Activer cette fonctionnalité pour l’intégralité des sites IIS :
    Configure-Backups -Enabled $true
  4. Spécifier l’emplacement des sauvegardes :
    Configure-Backups -BackupPath "{sitePathParent}\Backup_{siteName}"
    A l’aide de cette commande, les sauvegardes de l’application web « Neos-SDI.ContactManager » dont le contenu est à l’emplacement « c:\inetpub\Neos-SDI.ContactManager » seront stockés à l’emplacement « c:\inetPub\Backup_Neos-SDI.ContactManager ». Veillez à ne pas spécifier un dossier enfant à l’emplacement de l’application web sinon les sauvegardes seront supprimées à chaque déploiement.
    De plus, et dans le cas où le dossier de sauvegarde est situé sur un partage, vérifier la sécurité du dossier pour permettre aux utilisateurs IIS d’y effectuer la sauvegarde.
     
  5. Spécifier le nombre maximum de sauvegarde (recommandé) :
    Configure-Backups -NumberOfBackups 5
  6. Bloquer le déploiement des applications web si la sauvegarde échoue (recommandé) :
    Configure-Backups -ContinueSyncOnBackupFailure $false

La configuration du fournisseur BackupManager est terminée !
Dès lors, à chaque déploiement web, une sauvegarde du site est effectuée automatiquement. Il n’y a aucune manipulation supplémentaire pour les autres intervenants.

Restauration d’une sauvegarde

Dans le cas où un utilisateur souhaite restaurer une sauvegarde automatique, suivre les opérations suivantes :

  1. Pour récupérer la liste des sauvegardes disponibles, exécuter la commande suivante (remplacer les valeurs entre crochets) :
    msdeploy.exe -verb:dump -source:backupManager=[NomSiteIIS],wmsvc='[AdresseServiceGestionIIS]?site=[NomSiteIIS]',username='[NomUtilisateurIIS]',password='[MotDePasseUtilisateurIIS]' –allowUntrusted
    
    Le résultat pour notre environnement de test est le suivant :
    msdeploy.exe -verb:dump -source:backupManager=Neos-SDI.ContactManager,wmsvc='https://serveriis.neos-sdi.lan:8172/msdeploy.axd?site=Neos-SDI.ContactManager',username='Charles',password='P@ssw0rd' -allowUntrusted
    
    Remarques:
    • L’executable msdeploy se situe à l’emplacement suivant: "%ProgramFiles(x86)%\IIS\Microsoft Web Deploy V3 »
    • Le paramètre –allowUntrusted permet d’outrepasser la validation du certificat du service de gestion iis dans le cas de l’usage d’un certificat auto-généré.


  1. Pour restaurer une sauvegarde, exécuter la commande suivante (remplacer les valeurs entre crochet) :
    msdeploy.exe -verb:sync -source:backupManager -dest:backupManager=[NomSiteIIS]/[NomFichierZip],wmsvc='[AdresseServiceGestionIIS]?site=[NomSiteIIS]',username='[NomUtilisateurIIS]',password='[MotDePasseUtilisateurIIS]',connectionString=[ChaineConnexionSQL]' -skip:xpath=dirPath[@path='[DossierNonRestauré]'] -allowUntrusted
    
    Le résultat pour notre environnement de test est le suivant :
    msdeploy.exe -verb:sync -source:backupManager -dest:backupManager=Neos-SDI.ContactManager/msdeploy_2013_07_08_12_14_27.zip,wmsvc='https://10.24.0.10:8172/msdeploy.axd?site=Neos-SDI.ContactManager',username='Charles',password='P@ssw0rd',connectionString='Server=10.24.0.11;Database=ContactManager;User ID=ServerIIS;Password=P@ssw0rd' -skip:xpath=dirPath[@path='App_Data'] -allowUntrusted
    
    Remarques:
    • L’option “connectionString” est obligatoire si la sauvegarde contient une sauvegarde de base de données ;
    • Le paramètre « skip » permet de laisser en l’état un dossier lors de la restauration.

Sauvegarde manuelle

Il est possible d’effectuer une sauvegarde manuelle d’une application web et d’y associer des fournisseurs différents à ceux utilisés lors du déploiement web.
Dans notre cas, nous souhaitons effectuer une sauvegarde manuelle avec tout le contenu du site IIS ainsi que la base de données complète (schéma et données).

  1. Préparer un fichier manifest (format xml):
    <?xml version="1.0" encoding="utf-8"?>
    <m>
    <iisApp path="Neos-SDI.ContactManager" />
    <dbFullSql path="Server=10.24.0.11;Database=ContactManager;User ID=ServerIIS;Password=P@ssw0rd" IncludeHeaders="true" IncludeDatabaseContext="true" />
    </m>
    Nous avons déclaré deux fournisseurs (iisApp et dbFullSql).
    Pour le fournisseur dbFullSql, nous y spécifions la chaine de connexion de la base de données et nous y avons ajouté des options de script SMO supplémentaires.
    Par défaut, les options suivantes sont activées :
    • ScriptSchema
    • ScriptData
    • ScriptDropFirst
    • DriAll
    • Triggers
    • Indexes
    • NoFileGroup

    La liste complète des options de script SMO est disponible à l’adresse suivante : http://msdn.microsoft.com/en-us/library/Microsoft.SqlServer.Management.Smo.ScriptingOptions_properties.aspx
     
  2. Exécuter la commande suivante (remplacer les valeurs entre crochets) :
    msdeploy -verb:sync -source:backupManager=[EmplacementFichierManifest] -dest:backupManager=[NomSiteIIS],wmsvc='[AdresseServiceGestionIIS]?site=[NomSiteIIS]',username='[NomUtilisateurIIS]',password='[MotDePasseUtilisateurIIS]' –allowUntrusted
    Le résultat pour notre environnement de test est le suivant :
    msdeploy -verb:sync -source:backupManager=E:\Manifests\manifest.xml -dest:backupManager=Neos-SDI.ContactManager,wmsvc='https://10.24.0.10:8172/msdeploy.axd?site=Neos-SDI.ContactManager',username='Charles',password='P@ssw0rd' -allowUntrusted
    

 
Pour restaurer cette sauvegarde manuelle, la procédure est identique à une sauvegarde automatique.
 

Conclusion

Dans cet article, nous avons mis en œuvre une solution complète et simple de sauvegarde et de restauration des applications web hébergées sur un serveur qui possède le rôle IIS.

Pour notre dernier article, nous étudierons le déploiement d’une application web lors d’une compilation tfs (build tfs).

Tags: , , ,

Développement

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

by Charles BARJANSKY 26. juin 2013 03:56

Préambule

Cet article complète la série des 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 qui détient le rôle IIS.

Pour rappel, 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 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 2"

L’objectif de cette seconde partie est de prendre connaissance des différentes solutions pour la création ou la mise à jour d’une base de données lors du déploiement d’une application web via l’outil Microsoft Web Deploy. Ensuite, pour certaines solutions sélectionnées, nous découvrirons la mise en œuvre.

Les solutions disponibles sont :

  • Migration Entity Framework Code First ;
  • Fournisseur Web Deploy dbDacFx ;
  • Fournisseur Web Deploy dbFullSql.

Les solutions mises en œuvre dans cet article sont :

  • Fournisseur Web Deploy dbDacFx ;
  • Fournisseur Web Deploy dbFullSql.

Description des solutions

Migration Entity Framework Code First

Lors de l’usage de la solution Entity Framework Code First (pour rappel, une solution d’ORM dans laquelle les interactions avec la base de données ainsi que les manipulations des données sont gérées uniquement par du code), le développeur peut déclarer la stratégie de création ou de mise à jour de la base de donnée à l’aide :

  • Des stratégies disponibles par défaut dans le framework Entity Framework :
    • DropCreateDatabaseAlways ;
    • DropCreateDatabaseWhenModelChanges ;
    • CreateDatabaseIfNotExists ;
    • MigrateDatabaseToLatestVersion.
  • De développements spécifiques qui définissent le processus de création ou de mise à jour de la base de données.

L’outil de déploiement Web Deploy peut, uniquement si la stratégie retenue par le développeur est "MigrateDatabaseToLatestVersion", se charger de la configuration de l’application web. Ainsi après le déploiement et lors du 1er démarrage de l’application web, la base de données sera automatiquement créé ou mise à jour avec les différentes évolutions des différentes entités que composent le contexte Entity Framework.

Cette fonctionnalité est disponible depuis la version 4.3 d’Entity Framework.

Fournisseur Web Deploy dbDacFx

Un fournisseur Web Deploy qui réalise :

  • Lors du déploiement initial, créer les tables et les objets à l’identique d’une base de données source sur la base de donnée cible ;
  • Lors des déploiements suivants, un traitement différentiel du schéma entre la base de données source et une base de données cible.

Par défaut, le fournisseur n’est pas destructeur, c’est-à-dire ne supprime pas de données en base. De plus, cette option permet l’exécution des scripts SQL complémentaires.

Fournisseur Web Deploy dbFullSql

Un fournisseur Web Deploy simple qui accomplit uniquement le déploiement initial d’une base de donnée (schéma avec ou sans données).

Mise en œuvre des solutions

Pré-Requis

Pour permettre le succès du déroulement des deux mises en œuvre étudiées (dbFullSql & dbDacFx), veuillez suivre les tâches suivantes :

  1. Suivre l’article "Mise en œuvre de l’outil de déploiement web IIS (Microsoft Web Deploy 3 .0) – Partie 1" ;
  2. Installation du package “Microsoft® System CLR Types for Microsoft® SQL Server® 2012 SP1” (disponible à l’adresse suivante : http://www.microsoft.com/en-us/download/details.aspx?id=35580) sur le/les serveurs qui détiennent le rôle IIS ;
  3. Installation du package “Microsoft® SQL Server® 2012 SP1 Shared Management Objects” (disponible à l’adresse suivante : http://www.microsoft.com/en-us/download/details.aspx?id=35580) sur le/les serveurs qui détiennent le rôle IIS ;
  4. Obtention des paramétrages de connexion d’un utilisateur SQL ayant les droits de création de base de données (dbcreator) et de modification des informations de sécurité (securityadmin) sur une instance Microsoft SQL Server (différent du paramétrage de connexion existant de l'application web).

De plus, pour la mise en œuvre de la solution du fournisseur dbDacFx, veuillez effectuer les tâches complémentaires suivantes :

  1. Installation du package “Microsoft® SQL Server® 2012 SP1 Transact-SQL ScriptDom” (disponible à l’adresse suivante : http://www.microsoft.com/en-us/download/details.aspx?id=35580) sur le/les serveurs qui détiennent le rôle IIS ;
  2. Installation du package “Microsoft® SQL Server® 2012 Data-Tier Application Framework (March 2013)” (disponible à l’adresse suivante : http://www.microsoft.com/en-us/download/details.aspx?id=36842) sur le/les serveurs qui détiennent le rôle IIS.

Remarques : veillez à redémarrer le/les serveurs qui détiennent le rôle IIS à la fin des installations des packages.

Mise en œuvre de la solution fournisseur Web Deploy dbFullSql

Création d’une délégation du fournisseur dbFullsql

  1. Dans la console "Gestionnaire des services Internet (IIS)" ("Internet Information Services (IIS)") : 
    • Sélectionner le serveur désiré ;
    • 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").
    IIS Home
  2. Dans la vue "Délégation du service de gestion" ("Management Service Delegation"), cliquer sur "Ajouter une règle... " (" Add Rule... ") ;
    Management Service Delegation
  3. Dans la fenêtre "Ajouter une règle" ("Add Rule"), sélectionner "Règle vide" ("Blank rule") et cliquer sur "OK" ;
    Add Rule
  4. Dans la fenêtre "Ajouter une règle" ("Edit Rule"):
    • Fournir les informations suivantes :
      • "Fournisseurs" ("Providers") :
        1. "dbFullSql".
      • Actions :
        1. "*".
      • "Type de chemin" ("Path Type") :
        1. "Chaîne de connexion" ("Connection String").
      • "Chemin d’accès" ("Path") :
        1. "Datasource=".
      • "Type d’identité" ("Identity Type") :
        1. "Current User".
    • Cliquer sur "OK".
    Edit Rule
  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" ;
    Add User To Rule
  6. La règle de délégation est créée avec l’utilisateur associé.
    Management Service Delegation

Activer la publication Web Deploy pour un site IIS

  1. Suivre la procédure "Activer la publication Web Deploy pour un site IIS" de l’article "Mise en œuvre de l’outil de déploiement web IIS (Microsoft Web Deploy 3 .0) – Partie 1". Aucune information supplémentaire n’est nécessaire pour la publication à l’aide du fournisseur dbFullSql.

Configurer la publication SQL (Visual Studio 2012)

  1. Dans votre solution Visual Studio 2012, effectuer un clic droit sur le projet Web et sélectionner l’option “Propriétés ("Properties") ;
    Visual Studio 2012
  2. Dans l’onglet des propriétés de votre projet web : 
    1. Cliquer sur l’onglet "Package/Publication SQL" ("Package/Publish SQL") ;
    2. Sélectionner la configuration de solution correspondant à celle de votre déploiement web ("Release" par exemple") ;
    3. Cliquer sur le bouton "Activer cette page" ("Enable this Page") ;
      Publish SQL
    4. En fonction de votre l’application web, ajouter les entrées de base de données que vous souhaitez déployer à l’aide des boutons "Importer depuis Web.config" ("Import from Web.config") ou "Ajout” ("Add") ;
      Database Entries
    5. Spécifier pour chaque entrée de base de données ("Database Entries"):
      1. La chaine de connexion SQL (avec les informations de l’utilisateur SQL ayant les droits "dbcreator" et "security admin") sur la base de données de destination ;
         
        Remarques :
        • Celle-ci ne sera utilisée uniquement pour le déploiement de la base de données et ne remplace pas la chaîne de connexion présente dans le fichier de configuration ;
        • Cette chaîne de connexion doit fonctionner à partir du serveur qui détient le rôle IIS (car le déploiement est effectué à partir de l’instance IIS du serveur et non à partir de la machine en local).
      2. Les sources :
        1. Autogénéré (à partir de votre base de données développement) ;
        2. Script SQL (ajout d’un script SQL).
           
          Remarques :
          • Le script autogénéré est effectué à partir de l’instance Visual Studio 2012 (et non à partir fonctionner à partir du serveur qui détient le rôle IIS) ;
          • Le script autogénéré nécessite les informations supplémentaires :
            • Chaine de connexion ;
            • Type de script généré (schéma seul, schéma et données ou données uniquement).
        Pull data and or schema

Publication de l’application Internet (Visual Studio 2012)

  1. Suivre la procédure "Publication de l’application Internet (Visual Studio 2012)" de l’article "Mise en œuvre de l’outil de déploiement web IIS (Microsoft Web Deploy 3 .0) – Partie 1". Aucune information supplémentaire n’est nécessaire pour la publication à l’aide du fournisseur dbFullSql.

Mise en œuvre de la solution fournisseur Web Deploy dbDacFx

Création d’une délégation du fournisseur dbDacFx

  1. Dans la console "Gestionnaire des services Internet (IIS)" ("Internet Information Services (IIS)") : 
    • Sélectionner le serveur désiré ;
    • 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").
    IIS Home
  2. Dans la vue "Délégation du service de gestion" ("Management Service Delegation"), cliquer sur "Ajouter une règle... " (" Add Rule... ") ;
    Management Service Delegation
  3. Dans la fenêtre "Ajouter une règle" ("Add Rule"), sélectionner "Règle vide" ("Blank rule") et cliquer sur "OK" ;
    image
  4. Dans la fenêtre "Ajouter une règle" ("Edit Rule"):
    • Fournir les informations suivantes :
      • "Fournisseurs" ("Providers") :
        1. "dbDacFx".
      • Actions :
        1. "*".
      • "Type de chemin" ("Path Type") :
        1. "Chaîne de connexion" ("Connection String").
      • "Chemin d’accès" ("Path") :
        1. "Datasource=".
      • "Type d’identité" ("Identity Type") :
        1. "Current User".
    • Cliquer sur "OK".
    Edit Rule
  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" ;
    Add User To Rule
  6. La règle de délégation est créée avec l’utilisateur associé.
    Management Service Delegation

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") ;
    IIS Site
  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 "..." ;
    Configure Web Deploy Publishing
  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" ;
    image
  4. A l’emplacement "Insérer la chaîne de connexion SQL Server utilisé pour la publication" ("Enter SQL Server connection string to be used for publishing", cliquer sur le bouton "..." ;
    Configure Web Deploy Publishing
  5. Dans la fenêtre "Editer la chaine de connexion" ("Edit Connection String"), utiliser les différentes options pour créer/éditer la chaîne de connexion et cliquer sur "OK" ;
    Edit Connection String
  6. Dans la fenêtre "Activer la publication Web Deploy" ("Configure Web Deploy Publishing"), vérifier les différentes informations et cliquer sur "Configurer" ("Setup") ;
    Configure Web Deploy Publishing
  7. Vérifier le résultat puis cliquer sur "Fermer" ("Close") ;
    image
  8. Vous pouvez désormais rendre disponible le fichier généré à la personne désirée.
    PublishSettings File


 
 
 
 
 
 
 
 
 
 

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 “Publier…” ("Publish...") ;
    image
  2. L’assistant “Publier le site Web” ("Publish Web") s’affiche. Cliquer sur le bouton "Importer..." ("Import...") et sélectionner le fichier généré avec l’extension .publish ;
    image
  3. A l’étape "Connexion" ("Connection"), saisir le mot de passe, valider les informations via le bouton "Valider la connexion" ("Validate Connection") puis cliquer sur le bouton "Suivant >" ("Next >") ;
    image
  4. A l’étape "Paramètres" ("Settings"), cocher la case "Mettre à jour la base de donnée" ("Update database") et cliquer sur le bouton "Suivant >" ("Next >") ;
    image
  5. A l’étape "Aperçu" ("Preview"), vous pouvez visualiser les modifications apportées à la base de données en cliquant sur le lien “Prévisualiser la base de données” ("Preview Database") ;
  6. Pour terminer, cliquez sur “Publier” ("Publish") ;
    image
  7. A la fin de la publication, le navigateur est lancé pour permettre de vérifier le bon fonctionnement du site.
    image

Conclusion

Dans cet article, nous avons identifié et mise en œuvre les différentes solutions disponibles de déploiement d’une base de données associée à une application web à l’aide de l’outil Microsoft Web Deploy.

Nous aborderons dans le prochain article la sauvegarde automatique de l’application lors de chaque déploiement sur la plateforme IIS à l’aide de l’outil Microsoft Web Deploy.

Tags: , , ,

Développement | Infrastructure

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

Tags: , , , , , ,

Développement

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

Neos-SDI  Neos-SDI