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

13 réflexions au sujet de « Création d’un type de contenu externe et utilisation du secure store service via Visual Studio 2012 »

  1. Ping : Création d’une liste externe SharePoint 2013 en programmation | SharePoint 2013 - Blog technique d'Alexandre DAVID

  2. Ping : Utilisation de Business Connectivity Services dans SharePoint 2013 | SharePoint 2013 - Blog technique d'Alexandre DAVID

  3. Ping : Configuration du Secure Store Service et ajout d’une application afin de créer un type de contenu externe | SharePoint 2013 - Blog technique d'Alexandre DAVID

  4. Ping : Créer une colonne de données externes et la référencer dans un type de contenu | SharePoint 2013 - Blog technique d'Alexandre DAVID

  5. Ping : Créer une colonne de données externes et la référencer dans une liste | SharePoint 2013 - Blog technique d'Alexandre DAVID

  6. Ping : Ajout d’un filtre de recherche pour un type de contenu externe SharePoint 2013 | SharePoint 2013 - Blog technique d'Alexandre DAVID

  7. Ping : Utilisation des workflows SharePoint 2013 | SharePoint 2013 - Blog technique d'Alexandre DAVID

  8. Ping : Création d’un workflow de type State Machine (machine à état) SharePoint 2013 | SharePoint 2013 - Blog technique d'Alexandre DAVID

  9. Ping : Event Handler de modification des autorisations sur les tâches d’un workflow SharePoint | SharePoint 2013 - Blog technique d'Alexandre DAVID

  10. Ping : Comment afficher les tâches affectées à un utilisateur SharePoint et à ses groupes | SharePoint 2013 - Blog technique d'Alexandre DAVID

  11. Ping : SharePoint 2013 et l’agrégation de données provenant du SI puis exposition à une application mobile | SharePoint 2013 - Blog technique d'Alexandre DAVID

  12. JB

    Super article !!! J’ai pu facilement mettre en place du BCS sur une table SQL avec du Linq To SQL en quelques heures grâce à ton article. Encore merci.

    J’ai cependant une question, j’ai vu qu’il était possible de s’abonner à des évènements de mise à jour déclenchés côté SQL, mais tous les exemples sont pour des Apps SharePoint. :
    Est il alors possible d’envisager de lever des évènements côté SharePoint lors d’une mise à jour de contenu côté SQL pour une bonne vieille solution WSP de ferme ?

    Répondre

Laisser un commentaire

Entrez vos coordonnées ci-dessous ou cliquez sur une icône pour vous connecter:

Logo WordPress.com

Vous commentez à l'aide de votre compte WordPress.com. Déconnexion / Changer )

Image Twitter

Vous commentez à l'aide de votre compte Twitter. Déconnexion / Changer )

Photo Facebook

Vous commentez à l'aide de votre compte Facebook. Déconnexion / Changer )

Photo Google+

Vous commentez à l'aide de votre compte Google+. Déconnexion / Changer )

Connexion à %s