Archives du mot-clé développement

Création d’un type de contenu externe et utilisation du secure store service via Visual Studio 2012


Cet article fait partie d’une série concernant la mise en oeuvre de liste externes et de workflows avec SharePoint. Les étapes seront passées en revue :

_______________________________________________________________________________________________

Après avoir vu comment créer une application dans le secure store service puis s’en servir pour créer un type de contenu externe avec l’interface utilisateur SharePoint Designer 2013, voyons maintenant comment faire de même en programmation avec Visual Studio 2012.

Pour cela nous allons créer un accès complet à la table « Person » de la base « AdventureWorks2012 » avec le  Secure Store Service  de la manière suivante  :

Création du modèle BDC avec classes LINQ d’accès aux données

1 – Créer un projet de type SharePoint 2013 Vide

Visual Studio 2012 SharePoint 2013

2 – Sélectionner « déployer en tant que solution de batterie » (en effet un type de contenu externe ne se déploie qu’au niveau « farm »).

SharePoint 2013 - spalexandre

3 – Ajouter un nouvel élément de type « Modèle de connectivité de données métiers » au projet.

 Modèle de connectivité de données métiers (solution de batterie uniquement) spalexandre

4  – Se connecter à la base de données cible (ici de type SQL Server pour AdventureWorks2012).spalexandre sharepoint 2013

SQL Server datasource visual studio 2012

connexion base de données sharepoint 2013

5 – Ajouter une classe d’accès aux données au projet (Ajouter nouvel élément -> données -> classes Linq To SQL). Ceci dépend du type de source de données auquel vous avez affaire. Choisissez celui qui correspond ou créer votre propre classe d’accès aux données.classes Linq to SQL

6 – une fois le .dbml ouvert, ouvrir l’explorateur de serveur, sélectionner celui qui concerne la connexion ajoutée puis la(les) table(s) qui vous intéresse et glisser la(les) sur le designer.

visual studio 2012 designer LINQ entity

Dans mon cas, 2 classes ont été créées dans le fichier AdventureWorks.deigner.cs  : DataContext et Person. Le modèle de données de la table a été transformé en modèle objet, la table « Person » pourra donc être manipulé comme un objet C#, le type de ses propriétés étant mappé sur le type de colonne de la table en base de données. Ceci permet de simplifier l’accès aux données et va permettre de construire facilement via des requête LINQ des méthodes de CRUD pour notre entité.

7 – Supprimer les entités par défaut du modèle BDC. Pour cela ouvrir le designer en double cliquant sur « AventureWorksPerson » puis clic droit sur l’entité -> supprimer. Supprimer également les fichier Entity1.cs et Entity1Service.cs dans l’explorateur de solutions.

Delete entity visual studio 2012

8 – Ajouter une nouvelle entité sur le designer via la boite à outil en glisser déposer puis la renommer via la fenêtre de propriétés et définir un identificateur via clic droit sur le modèle puis définir son type.

Business data connectivity SharePoint 2013

propriété visual studio 2012

Propriétés visual studio 2012 BDCM

visual studio 2012 bcs

Accès au Secure Store  Service et récupération des informations d’authentification

1 – Ajouter un nouvel élément au projet de type Code -> Classe C#.

secure store service programmatically

2 – référencer les DLL suivantes :

  • Microsoft.Office.SecureStoreService située dans « C:\Windows\Microsoft.NET\assembly\GAC_MSIL\Microsoft.Office.SecureStoreService\v4.0_15.0.0.0__71e9bce111e9429c »
  • Microsoft.BusinessData située dans « C:\Program Files\Common Files\microsoft shared\Web Server Extensions\15\ISAPI\ »

3 – Ajouter le code suivant au fichier :

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.SharePoint;
using System.Runtime.InteropServices;
using System.Security;
using Microsoft.BusinessData.Infrastructure.SecureStore;
using Microsoft.Office.SecureStoreService.Server;

namespace SPAlex.Labs
{
    public static class SecureStoreUtils
    {
        public static Dictionary<string, string> GetCredentials(string applicationID)
        {
            var credentialMap = new Dictionary<string, string>();
            SPSecurity.RunWithElevatedPrivileges(delegate()
            {
                SPServiceContext serviceContext = SPServiceContext.Current;
                var secureStoreProvider = new SecureStoreProvider { Context = serviceContext };
                using (var credentials = secureStoreProvider.GetCredentials(applicationID))
                {
                    var fields = secureStoreProvider.GetTargetApplicationFields(applicationID);
                    for (var i = 0; i < fields.Count; i++)
                    {
                        var field = fields[i];
                        var credential = credentials[i];
                        var decryptedCredential = ToClrString(credential.Credential);

                        credentialMap.Add(field.Name, decryptedCredential);
                    }
                }
            });
            return credentialMap;
        }

        public static string ToClrString(this SecureString secureString)
        {
            var ptr = Marshal.SecureStringToBSTR(secureString);

            try
            {
                return Marshal.PtrToStringBSTR(ptr);
            }
            finally
            {
                Marshal.FreeBSTR(ptr);
            }
        }

        public static string getSQLConnectionString(string applicationID, string serverName, string dataBaseName)
        {
            Dictionary<string, string> creds = GetCredentials (applicationID);
            string connectionString = string.Format(@"Data Source={0};Initial Catalog={1};User Id={2};Password={3};",
                serverName,
                dataBaseName,
                creds["Nom d'utilisateur SQL"],
                creds["Mot de passe SQL"]);

            return connectionString;
        }
    }
}

Attention à utiliser les mêmes noms de champs que ceux renseignés lors de la création de votre application dans le Secure Store Service pour récupérer les credentials dans le méthode getConnectionString.

Création des méthodes de CRUD pour le type de contenu externe

Recherche d’élément spécifique (readItem)

1 – Afin de permettre au service SharePoint de pouvoir faire des opérations de CRUD sur notre base de donnée externe, il faut définir les méthodes qui permettent d’accéder à ces données via le modèle de données créé précédemment et basé sur notre connexion. Dans notre cas nous allons créer des méthodes de recherche. Pour cela cliquer sur la partie « méthodes » de votre entité afin de voir apparaître la fenêtre ‘« détails de la méthode BDC » puis sélectionner « Créer une méthode de recherche spécifique » comme suit :

détails de la méthode BDC Visual Studio 2012

2 – Modifier le type de paramètre de retour de cette méthode afin de décrire l’ensemble des propriétés de l’objet « personne ».

modifier le descripteur de type visual studio 2012

Dans la fenêtre propriétés pour la valeur « Nom de type » sélectionner projet actif -> PERSONNE afin de mapper le type d’objet retourné avec l’entité de mapping LINQ.

descripteur de type business data connectivity

3 – Ajouter tous vos descripteurs de types correctement typés 🙂 via l’explorateur de BDC.

explorateur bdc visual studio 2012 - descripteur de type

Ajouter le premier en modifiant son nom / son type et en le sélectionnant en tant qu’identifiant dans la fenêtre propriétés.

identificateur explorateur de BDC visual studio 2012

Ajouter tout ceux qui correspondront aux éléments qui vous voudrez utiliser dans votre liste externe SharePoint 2013. Ne pas oublier de les typer.

Descripteur de type visual studio 2012

4 – Dans le fichier PersonneServices.cs déclarer les constantes suivantes (qui peuvent être stockées en SPPersistedObject ou dans le propertyBag niveau Ferme via un featureReceiver) :


private const string serverName = @"SPALEXANDRE\SQLSHAREPOINT";
private const string dataBaseName = "AdventureWorks2012";
private const string sssAppId = "AdventureWorks2012";

5 – Transformer les méthode ReadItem créée automatiquement avec le code suivant :

public static PERSONNE ReadItem(int BusinessEntityID)
{
string connectString = SecureStoreUtils.getSQLConnectionString(sssAppId, serverName, dataBaseName);
AventureWorksDataContext dataContext = new AventureWorksDataContext(connectString);

PERSONNE myPersonne =
(from p in dataContext.PERSONNE.AsEnumerable()
where p.BusinessEntityID == BusinessEntityID
select p).Single();
return myPersonne;
}

Cette méthode récupère un enregistrement à partir de la table personne de la BDD AdventureWorks  en fonction de son ID. La chaîne de connexion du dataContext Linq  est construite avec les credentials du l’utilisateur SQL stocké dans le Secure Store Service pour cette application.

Résultat :

recherche élément type de contenu externe

Recherche globale (ReadList)

1 – ajouter une nouvelle méthode de recherche.

méthode de recherche BDC

2 – remplacer le code de la méthode générée par le code suivant :


public static IEnumerable ReadList()
{
string connectString = SecureStoreUtils.getSQLConnectionString(sssAppId, serverName, dataBaseName);
AventureWorksDataContext dataContext = new AventureWorksDataContext(connectString);

IEnumerable pList = from p in dataContext.PERSONNE.Take(1000) select p;
return pList;
}

Cette méthode récupère les 1000 premières lignes de la table SQL (pour rappel au delà de 2000 ça n’est pas possible).

Une fois les méthodes de recherche créée la solution peut être compilée, déployée et testé en créant une liste externe basée sur ce nouveau type de contenu manuellement dans le site cible. Les données devrait être accessible sauf problème de droits d’accès (plutôt récurrents dans ce cas).

Résultat :

Recherche type de contenu externe

Création (Create)

1 – Ajouter une méthode de création dans la fenêtre « Détails de la méthode BDC »  comme suit :

BCS create

2 – remplacer le code de la méthode par le code suivant :


        public static PERSONNE Create(PERSONNE newPersonne)
        {
            string connectString = SecureStoreUtils.getSQLConnectionString(sssAppId, serverName, dataBaseName);
            AventureWorksDataContext dataContext = new AventureWorksDataContext(connectString);

            PERSONNE myPers = new PERSONNE();
            myPers.BusinessEntityID = newPersonne.BusinessEntityID;
            myPers.FirstName = newPersonne.FirstName;
            myPers.LastName = newPersonne.LastName;

            //propriétés annexes ajoutées de par les contraintes de base de données
            myPers.ModifiedDate = DateTime.Now;
            myPers.PersonType = "EM";
            myPers.EmailPromotion = 0;
            myPers.NameStyle = false;
            myPers.rowguid = new Guid();

            dataContext.PERSONNE.InsertOnSubmit(myPers);
            dataContext.SubmitChanges();
            return myPers;
        }

Si vous aviez déjà créé une liste externe dans SharePoint afin de tester les éléments de recherche, il faudra la supprime et la recréer pour tester cette méthode sinon vous aurez l’erreur suivante : « Impossible de trouver le formulaire de création par défaut pour la liste XXX ».

résultat : newform BCS

Mise à jour (Update)

1 – Ajouter une méthode de mise à jour dans la fenêtre « Détails de la méthode BDC »  comme suit :

BCS update

2 – Modifier les propriété du paramètre en INPUT si le champs ID n’est pas en lecture seule (ce qui est mon cas) afin de cocher la prorpiété champs de pré-mise à jour (PreUpdaterField)  à TRUE.

PreUpdaterField true

3 – remplacer le code de la méthode par le code suivant :

public static void Update(PERSONNE personne)
 {
 string connectString = SecureStoreUtils.getSQLConnectionString(sssAppId, serverName, dataBaseName);
 AventureWorksDataContext dataContext = new AventureWorksDataContext(connectString);

PERSONNE myPersonneToUPdate =
 (from p in dataContext.PERSONNE.AsEnumerable()
 where p.BusinessEntityID == personne.BusinessEntityID
 select p).Single();

myPersonneToUPdate.FirstName = personne.FirstName;
 myPersonneToUPdate.LastName = personne.LastName;
 dataContext.SubmitChanges();

}

résultat :

update type de contenu externe programmation

Suppression (Delete)

1 – Ajouter une méthode de suppression dans la fenêtre « Détails de la méthode BDC »  comme suit :

BCS Delete

2 – remplacer le code de la méthode par le code suivant :


public static void Delete(int businessEntityID)
        {
            string connectString = SecureStoreUtils.getSQLConnectionString(sssAppId, serverName, dataBaseName);
            AventureWorksDataContext dataContext = new AventureWorksDataContext(connectString);

            PERSONNE myPersonneToDelete =
             (from p in dataContext.PERSONNE.AsEnumerable()
              where p.BusinessEntityID == businessEntityID
              select p).Single();

            dataContext.PERSONNE.DeleteOnSubmit(myPersonneToDelete);
            dataContext.SubmitChanges();
        }
    }

résultat :

Delete external content type

N.B : Cette méthode n’est pas fonctionnelle aujourd’hui pour ce type d’élément dans le cas de la base AdventureWorks2012 car il faudrait supprimer toutes les dépendances associées. Mais le principe est là 🙂

Pour déploiement et test via une liste externe créé par programmation c’est par ici.

Publicités

Créer son environnement de développement SharePoint 2013 sur VMWare Player / Windows Seven


Je ne vais pas refaire un guide complet pour créer cet environnement car il en existe de très bons et notamment celui proposé par Critical Path gratuitement Lien direct / accès à la section Membre (il faut être membre du site pour accéder au fichier  mais l’inscription est rapide et gratuite).

Ce que permet de faire le guide d’installation

Ce guide fournit des instructions en ANGLAIS pur monter 1 VM via Hyper-V (qui ne peut tourner que sur un OS client de type Windows 8) avec les éléments suivants :

  • Windows Server 2012 + ajout du rôle contrôleur de domaine AD DS 2012 (Active Directory Domain Services) + quelques personnalisations bien utiles (désactivation du firewall et de la sécurité renforcée IE et activation du bureau à distance). J’ai toujours pensé qu’il fallait impérativement un AD à part (une autre VM) pour voir fonctionner certains service tels que le User Profile Service mais, a priori, il n’en est rien. Par contre c’est prohibé pour une prod, on est d’accord.
  • Installation de SQL Server 2012 SP1 spécifique pour SharePoint (on ne sélectionne que les composants nécessaires)
  • Ajout des bases de données AdventureWorks sur l’instance et création du datawhareHouse
  • Créer les OU / comptes de services / comptes utilisateurs avec relation hiérarchique / groupes d’utilisateurs -> tout ceci via de super scripts powershell !
  • désactivation du loopbackcheck, création d’un certificat auto signé,  ajout d’un enregistrement DNS wildcard pour mon nom de domaine avec l’adresse IP locale -> encore via powershell.
  • Installation des pré requis SharePoint 2013 (sous condition d’être connecté à Internet)
  • Installation de SharePoint 2013
  • Installation de Service Bus et de Workflow Manager via le Web Platform Installer
  • Installation du cumulative Update de Mars 2013 et d’Avril 2013
  • Création de la ferme SharePoint 2013
  • Création du service de souscription pour les apps (powershell)
  • Création des webapps et collections de sites (powershell)
  • Configuration de workflow Manager (powershell)
  • Configuration du Service de synchronisation des proifls utilisateur (powershell)
  • Configuration du service de recherche
  • Activation de reporting services et création de l’application de service Sharepoint associée
  • Installation de Fiddler 2.0
  • Installation de SharePoint Designer 2013 et de l’update de Février 2013
  • Installation , update et configuration de Visual Studio 2012
  • Installation d’Office 2013 et Visio 2013

Les modifications apportées avec VMWare Player et Environnement en français personnalisé

Etant contraint, pour le moment, à utiliser Windows Seven, j’ai créé ma VM via VMWare Player (gratuit) et lui ai alloué uniquement 10Go de RAM ce qui était largement suffisant pour l’installation. Les modifications que j’ai apporté au guide ont été les suivantes :

  • installation de l’OS Windows Server 2012 FR Standard automatisée par VMWare player car j’ai eu l’erreur « Un pilote de périphérique de lecteur CD/DVD requis est manquant » lorsque j’essayais de l’installer manuellement. J’ai pourtant vérifié ce lien et comparer les hash ISO entre ma machine et le serveur MSDN, ils étaient identiques.
  • pas de création d’une 2e carte réseau virtuelle (pour créer un réseau virtuel) mais utilisation de la carte réseau par défaut en NAT
  • obligation de saisir mon adresse DNS d’entreprise en tant que DNS secondaire ainsi que mes paramètres de proxy d’entreprise dans IE puis d’exécuter la commande suivante qui permet à Windows de se connecter à Internet  :
Netsh winhttp import proxy source=ie

Si cette manip ne fonctionne pas je vous recommande Charles Proxy, un web debugger qui m’a sortit de la panade plus d’une fois (il nécessite par contre le javaruntime) car il se substitue au proxy. Tout ceci devra bien entendu être téléchargé depuis votre poste client puis installé sur votre VM.

  • Lors de l’installation de SQL Server 2012, j’ai rencontré l’erreur « Erreur lors de l’activation de la fonctionnalité Windows : NetFx3, Erreur Code : –2146498298 « . Je l’ai résolue grâce à un billet de Mitch Garvis (il faut remonter l’iso Windows Server 2012 et lancer l’installation en ligne de commande, l’interface ne suffit pas).
  • Etant sur un environnement totalement français (OS, SQL et SP) et ayant personnalisé mon nom de domaine ainsi que mon nom de machine l’ensemble des scripts powershells ont dû être passés en revue pour changer les noms de groupe AD (ex : administators -> administrateurs) ainsi que le nom de machine et ndd + les noms de services d’application (ex : « Microsoft SharePoint Foundation Subscription Settings Service » devient « Paramètres d’abonnement de Microsoft SharePoint Foundation »). La listes des noms de vos services et de vosapplications de services peut être récupérée via une commande powershell.
script powershell

Script powershell permettant de créer les webapps et les collections de sites

  • Démarrage manuel du service de synchronisation des profils utilisateurs dans l’admin centrale après l’exécution du script « Script06_CreateUserProfileServiceApplication », ce afin de pouvoir créer la connexion à l’AD car il ne s’était pas démarré tout seul.

synchronisation de profil utilisateur

et Tadaaaa :

Page accueil sharepoint 2013

Mon site SharePoint 2013

Si vous voulez les scripts powershell modifiés pour un environnement en Français n’hésitez pas à me les demander par email !

Il faut compter une bonne journée (en ayant pris soin de tout télécharger au préalable ou en ayant une bonne bande passante) pour effectuer cette installation complète.

Ce guide est excellent de par son niveau de détail et l’aide précieuse des scripts powershell fournit, vous pouvez y aller les yeux fermés !