0. Introduction▲
Dans cet article, nous allons voir comment écrire nos propres fichiers ADF.
Dans un premier temps, nous allons voir comment créer un fichier ADF « de base », mais cependant pleinement fonctionnel.
Ensuite, nous verrons comment améliorer notre fichier afin de rajouter des fonctionnalités à notre application, afin que cette dernière soit pleinement fonctionnelle, et qu'elle puisse être utilisée de manière optimale par nos utilisateurs finaux.
L'objectif de ce tutoriel est de créer une application qui va modéliser les ressources humaines de la société AdventureWorks, dont la base de données est fournie à titre d'exemple par Microsoft avec SQL Server, ou téléchargeable gratuitement sur CodePlex.
I. Pré requis▲
Les pré requis pour suivre ce tutoriel sont exactement les même que ceux de l'article précédent « A la découverte du BDC - Partie 1 » ;
De plus, cet article part du postulat que vous avez lu l'article précédent, que vous avez récupéré et installé les composants listés dans l'article précédent, et de manière générale, que suite à l'article précédent, vous vous êtes familiarisé avec les webparts du BDC et l'import d'un fichier ADF.
Si vous n'avez pas lu l'article précédent, je vous conseille vivement de le faire avant de poursuivre la lecture de celui-ci.
II. Préparation de notre environnement▲
Cet article se veut pédagogique et a pour objectif de familiariser le lecteur avec la création de fichier ADF, ainsi que de démystifier la complexité (inexistante) des fichiers ADF aux yeux du lecteur.
Partant de ce postulat, nous écrirons les fichiers ADF à la main, et chacun des bouts de code que nous utiliserons sera détaillé. Nous verrons très rapidement également quelques outils nous permettant de générer ces fichiers, mais l'objectif de cet article est encore une fois d'expliquer les éléments qui composent un fichier ADF au lecteur, afin que ce dernier puisse les prendre en main très facilement, aussi nous passerons très rapidement sur l'utilisation de ces outils.
Afin de nous faciliter la tache et éviter des erreurs de frappe inutiles, nous allons utiliser Visual Studio pour écrire nos fichiers ADF et sa fonctionnalité d'IntelliSense ; pour cela, nous allons récupérer nos schémas xsd spécifiques au BDC et les intégrer à Visual Studio.
Pour cela, rendez vous dans le répertoire « C:\Program Files\Microsoft Office Servers\12.0\Bin » et récupérez les fichiers bdcmetadata.xsd et bdcmetadataresource.xsd, puis collez les dans le répertoire « C:\Program Files\Microsoft Visual Studio 9.0\Xml\Schemas» de Visual Studio (2008 ; si vous utilisez VS 2005, remplacez 9.0 par 8.0).
Chacun de ces fichiers correspond a 1 type de fichier ADF que nous pouvons écrire (il y a donc 2 types de fichiers ADF différents, nous les verrons ultérieurement.).
Dorénavant, lorsque vous créez des fichiers ADF utilisez l'un ou l'autre de ces 2 fichiers, suivant votre besoin.
III. Présentation des fichiers ADF▲
Comme nous l'avons vu dans l'article précédent, les fichiers ADF vont nous permettre de décrire un LobSystem que nous souhaitons intégrer à SharePoint, afin que ce dernier puisse s'y connecter et nous restituer les informations sur notre portail web.
Nous avons également vu qu'il existe 2 types de fichiers ADF, les fichiers modèles (utilisant le schéma bdcmetadata.xsd), qui vont nous permettre de décrire notre LobSystem à proprement parler, et les fichiers ressources (utilisant le schéma bdcmetadataresource.xsd) qui vont éventuellement venir compléter notre fichier modèle en y rajoutant des informations sur la localisation (gestion de la langue), des propriétés (comme par exemple les données à afficher par défaut) et des autorisations, au travers d'ACE appartenant à des ACLs.
Dans cet article, nous allons procéder par étapes ; Nous allons tout d'abord commencer par écrire un fichier ADF minimal, mais fonctionnel, puis nous verrons comment améliorer l'application ainsi créée et par la même occasion modifier notre ADF existant afin qu'il devienne plus élaboré ; nous terminerons enfin par la création d'un fichier ADF de ressource pour notre application.
IV. Ecriture de notre fichier adf de base▲
L'objectif de cette 1ère partie va être de réaliser un fichier ADF simpliste, nous permettant de récupérer tous les départements de la société AdventureWorks.
L'application correspondant à ce fichier et mise à disposition de nos utilisateurs à l'issue de cette phase est illustrée ci-dessous.
Sans plus tarder, commençons à travailler !
Ouvrez donc Visual Studio et créez un nouveau fichier xml que nous appelons « bdc.adworks.hr.xml ».
Dans la fenêtre propriétés, sélectionnez « schémas », puis cliquez sur les « . », ensuite sélectionnez « bdcmetadata.xsd » comme illustré ci-dessous
Puis cliquez sur Ok. Dorénavant, l'IntelliSense est configurée sur votre fichier.
A présent, rentrons dans le vif du sujet.
IV-A. Création de l'élément racine, le LobSystem.▲
L'élément LobSystem est l'élément racine de notre fichier.
C'est cet élément qui va permettre d'identifier le système que nous souhaitons modéliser.
Dans notre cas, nous souhaitons représenter des éléments présents dans une base de données, nous allons donc utiliser la définition suivante :
<LobSystem
xmlns
=
"http://schemas.microsoft.com/office/2006/03/BusinessDataCatalog"
Type
=
"Database"
Version
=
"1.0.0.0"
Name
=
"AdventureWorksHumanResources"
>
</LobSystem>
L'attribut xmlns va représenter le namespace utilisé par le BDC ; il est rajouté automatique à l'insertion de l'élément. Attention, cette valeur ne doit pas être modifiée !
L'attribut Type va nous permettre de spécifier quel type de source de données nous souhaitons attaquer ; ici, nous allons attaquer la base de données AdventureWorks, donc la valeur de l'attribut sera Database ; l'autre valeur possible est WebService.
L'attribut Version va surtout être utile au niveau SharePoint ; il va nous permettre de pouvoir travailler de manière itérative sur nos fichiers adf ; lorsque nous modifions notre fichier, si nous essayons de le réimporter sans au préalable supprimer l'application de notre infrastructure SharePoint, nous obtenons un message d'erreur.
Pour y remédier, il nous suffit d'incrémenter la version de notre fichier, et le tour est joué !
L'attribut Name va simplement nous permettre de nommer notre application.
A cette étape, si nous importons notre application, nous obtenons ceci :
Une fois que nous avons défini notre LobSystem, il va falloir que nous décrivions où se situent les données et comment y accéder ; c'est l'élément LobSystemInstance qui va s'en charger.
Grossièrement, un LobSystem c'est :
- Une (ou plusieurs) LobSystemInstance, qui va nous permettre de spécifier les paramètres de connexion à notre source de données,
- Des entités, que l'on peut considérer (notamment dans le cadre d'une base de données) comme des objets similaires aux tables ou aux vues,
- Des associations, que l'on peut considérer (toujours dans le cadre d'une base de données) comme des relations entre tables.
Notre 1ère application sera donc composée d'une entité et d'une instance ; ultérieurement, nous ajouterons d'autres entités et modéliserons des associations.
IV-B. Création d'une instance de notre LobSystem.▲
Comme nous l'avons vu ci-dessus, nous allons utiliser l'élément LobSystemInstances pour décrire la manière de se connecter à notre LobSystem.
Le bout de code suivant, à insérer juste après l'élément LobSystem, se charge de cette opération.
<LobSystemInstances>
<LobSystemInstance
Name
=
"AdventureWorksHumanResourcesWebInstance"
>
<Properties>
<Property
Name
=
"AuthenticationMode"
Type
=
"Microsoft.Office.Server.ApplicationRegistry.SystemSpecific.Db.DbAuthenticationMode"
>
PassThrough</Property>
<Property
Name
=
"DatabaseAccessProvider"
Type
=
"Microsoft.Office.Server.ApplicationRegistry.SystemSpecific.Db.DbAccessProvider"
>
SqlServer</Property>
<Property
Name
=
"RdbConnection Data Source"
Type
=
"System.String"
>
.</Property>
<Property
Name
=
"RdbConnection Initial Catalog"
Type
=
"System.String"
>
AdventureWorks</Property>
<Property
Name
=
"RdbConnection Integrated Security"
Type
=
"System.String"
>
SSPI</Property>
</Properties>
</LobSystemInstance>
</LobSystemInstances>
L'élément LobSystemInstances va nous permettre de créer plusieurs instances ; Il existe cependant une contrainte à respecter lorsque nous travaillons avec plusieurs LobSystemInstances, c'est que toutes ces instances doivent avoir le même schéma. Il est donc impossible de représenter 2 instances différentes pour accéder à 2 bases de données différentes. L'objectif principal de travailler avec plusieurs instances est de spécifier des informations de connexion différentes.
L'élément LobSystemInstance représente donc une de ces instances ; l'attribut Name va permettre de nommer cette instance.
L'élément Properties va nous permettre de rajouter des propriétés au niveau de notre instance ; c'est notamment grâce à ces propriétés que nous allons pouvoir passer les paramètres de connexion. Ces propriétés auraient également pu être ajoutées directement dans un fichier ressource, cependant, étant donné que le fonctionnement de notre application dépend directement d'elles, nous allons les renseigner immédiatement.
Chacune des propriétés renseigne une information de connexion.
Ici, nous spécifions entre autre le type de serveur de base de données utilisé (ici SQL Server), le nom de notre Instance, et notre base de données.
Pour ceux d'entre vous qui ont l'habitude de travailler avec ado.net, ce bout de code doit vous paraitre familier.
Nous spécifions également La propriété AuthenticationMode, qui a ici la valeur PassThrough, ce qui spécifie à SharePoint de récupérer les informations utilisateur et de les passer directement à la source de données ; cette information est couplée avec la propriété Integrated Security qui vaut SSPI. Concrètement, lorsqu'un utilisateur va utiliser le BDC, SharePoint va transmettre directement à SQL Server les informations d'authentification windows de l'utilisateur ; ainsi, si au niveau SQL Server l'utilisateur a les autorisations nécessaires, il pourra accéder aux données depuis le BDC, sinon il recevra un message d'information.
Voila, notre instance est définie !
Passons maintenant à la création de notre entité.
IV-C. Création de l'entité département▲
Commençons donc par créer notre entité, juste en dessous de l'élément LobSystemInstances.
<Entities>
<Entity
Name
=
"Department"
>
</Entity>
</Entities>
Nous venons de représenter notre entité, cependant elle est totalement vide.
Pour faire la corrélation avec notre source de données (ici SQL Server), et spécifier toute la logique avec laquelle nous allons récupérer notre entité, il va falloir créer une (ou plusieurs) méthodes.
Une méthode va contenir plusieurs informations :
- La requête à exécuter nous permettant de ramener nos données,
- La définition de nos données représentées par des paramètres
- Les différentes variantes d'exécution de notre requête.
Si nous examinons la structure de la table départements dans la base de données, nous remarquons que nous avons 4 champs :
- DepartementID, de type smallint (int16 en .net),
- Name, de type nvarchar(50) (string en .net),
- GroupName, de type nvarchar(50) (string en .net),
- Et ModifiedDate, de type datetime (DateTime en .net).
Une requête comme la suivante devrait donc nous permettre de récupérer tous nos départements.
--sql.adworks.sql
--Connexion à la BD AdventureWorks
use
AdventureWorks
go
--Récupération de tous les départements.
select
DepartmentID, [Name]
as
[Service]
, GroupName as
Department, ModifiedDate
from
HumanResources.Department
go
Et voici d'ailleurs le résultat.
Grâce à ces informations, nous avons donc identifié toutes les informations nécessaires à la conception de notre entité ; notre requête SQL, notre paramètre de sortie, à savoir la table des départements, ainsi que la structure de notre table.
Voici le code complet de l'entité département.
<Entity
Name
=
"Department"
>
<Methods>
<Method
Name
=
"GetDepartments"
>
<Properties>
<Property
Name
=
"RdbCommandText"
Type
=
"System.String"
>
select DepartmentID, [Name] as [Service], GroupName as
Department, ModifiedDate
from HumanResources.Department
</Property>
<Property
Name
=
"RdbCommandType"
Type
=
"System.Data.CommandType"
>
Text</Property>
</Properties>
<Parameters>
<Parameter
Direction
=
"Return"
Name
=
"Departments"
>
<TypeDescriptor
TypeName
=
"System.Data.IDataReader, System.Data, Version=2.0.3600.0, Culture=neutral,
PublicKeyToken=b77a5c561934e089"
IsCollection
=
"true"
Name
=
"DepartmentDataReader"
>
<TypeDescriptors>
<TypeDescriptor
TypeName
=
"System.Data.IDataRecord, System.Data, Version=2.0.3600.0, Culture=neutral,
PublicKeyToken=b77a5c561934e089"
Name
=
"DepartmentDataRecord"
>
<TypeDescriptors>
<TypeDescriptor
TypeName
=
"System.Int16"
Name
=
"DepartmentID"
/>
<TypeDescriptor
TypeName
=
"System.String"
Name
=
"Service"
/>
<TypeDescriptor
TypeName
=
"System.String"
Name
=
"Department"
/>
<TypeDescriptor
TypeName
=
"System.DateTime"
Name
=
"ModifiedDate"
/>
</TypeDescriptors>
</TypeDescriptor>
</TypeDescriptors>
</TypeDescriptor>
</Parameter>
</Parameters>
<MethodInstances>
<MethodInstance
Name
=
"DepartmentFinderInstance"
Type
=
"Finder"
ReturnParameterName
=
"Departments"
/>
</MethodInstances>
</Method>
</Methods>
</Entity>
Nous nous servons donc des propriétés pour passer notre requête ; à noter qu'ici nous nous servons d'une requête de type texte, mais nous aurions également pu passer par les procédures stockées et renseigner uniquement la procédure stockée ; nous le ferons ultérieurement.
Ensuite nous créons un paramètre de retour, qui sera utilisé pour stocker le résultat de notre requête, que nous appelons Départements.
Puis nous définissons le type de notre paramètre ; ici nous créons un datareader, puis un datarow et enfin dans le datarow chacune des colonnes de notre table. Nous utilisons un datareader, car le BDC va utiliser ADO.Net pour récupérer nos données, et avec ADO.net, on peut soit utiliser un datareader, soit passer par un dataset pour récupérer nos données.
Nous avons choisi ici de passer par un datareader, mais nous aurions pu passer également par un dataset.
Enfin, nous créons une methodInstance de type finder, qui va nous permettre de récupérer toutes les données de notre table.
Une methodInstance est une instance particulière de la méthode à laquelle elle appartient ; on peut la voir comme l'exécution de la méthode à laquelle elle appartient, en y rajoutant des paramètres. De cette façon, grâce aux methodInstances, on va pouvoir exécuter de plusieurs façon différentes une même méthode, mais en faisant varier les paramètres en entrée. Pour l'instant, nous ne passons pas de paramètres, donc nous ne sommes pas capable de voir la subtilité que se cache derrière ce principe, mais patience.
Voila, c'est terminé !
Nous venons donc de créer notre 1er fichier ADF.
Voici le code complet du fichier.
<!--Fichier bdc.adworks.hr.minimal.xml-->
<LobSystem
xmlns
=
"http://schemas.microsoft.com/office/2006/03/BusinessDataCatalog"
Type
=
"Database"
Version
=
"1.0.0.0"
Name
=
"AdventureWorksHumanResources"
>
<LobSystemInstances>
<LobSystemInstance
Name
=
"AdventureWorksHumanResourcesInstance"
DefaultDisplayName
=
"AdventureWorks Human Resources Web Instance"
>
<Properties>
<Property
Name
=
"AuthenticationMode"
Type
=
"Microsoft.Office.Server.ApplicationRegistry.SystemSpecific.Db.DbAuthenticationMode"
>
PassThrough</Property>
<Property
Name
=
"DatabaseAccessProvider"
Type
=
"Microsoft.Office.Server.ApplicationRegistry.SystemSpecific.Db.DbAccessProvider"
>
SqlServer</Property>
<Property
Name
=
"RdbConnection Data Source"
Type
=
"System.String"
>
.</Property>
<Property
Name
=
"RdbConnection Initial Catalog"
Type
=
"System.String"
>
AdventureWorks</Property>
<Property
Name
=
"RdbConnection Integrated Security"
Type
=
"System.String"
>
SSPI</Property>
</Properties>
</LobSystemInstance>
</LobSystemInstances>
<Entities>
<Entity
Name
=
"Department"
>
<Methods>
<Method
Name
=
"GetDepartments"
>
<Properties>
<Property
Name
=
"RdbCommandText"
Type
=
"System.String"
>
select DepartmentID, [Name] as [Service], GroupName as Department, ModifiedDate
from HumanResources.Department
</Property>
<Property
Name
=
"RdbCommandType"
Type
=
"System.Data.CommandType"
>
Text</Property>
</Properties>
<Parameters>
<Parameter
Direction
=
"Return"
Name
=
"Departments"
>
<TypeDescriptor
TypeName
=
"System.Data.IDataReader, System.Data, Version=2.0.3600.0, Culture=neutral,
PublicKeyToken=b77a5c561934e089"
IsCollection
=
"true"
Name
=
"DepartmentDataReader"
>
<TypeDescriptors>
<TypeDescriptor
TypeName
=
"System.Data.IDataRecord, System.Data, Version=2.0.3600.0, Culture=neutral,
PublicKeyToken=b77a5c561934e089"
Name
=
"DepartmentDataRecord"
>
<TypeDescriptors>
<TypeDescriptor
TypeName
=
"System.Int16"
Name
=
"DepartmentID"
/>
<TypeDescriptor
TypeName
=
"System.String"
Name
=
"Service"
/>
<TypeDescriptor
TypeName
=
"System.String"
Name
=
"Department"
/>
<TypeDescriptor
TypeName
=
"System.DateTime"
Name
=
"ModifiedDate"
/>
</TypeDescriptors>
</TypeDescriptor>
</TypeDescriptors>
</TypeDescriptor>
</Parameter>
</Parameters>
<MethodInstances>
<MethodInstance
Name
=
"DepartmentFinderInstance"
Type
=
"Finder"
ReturnParameterName
=
"Departments"
/>
</MethodInstances>
</Method>
</Methods>
</Entity>
</Entities>
</LobSystem>
Vous pouvez dès à présent l'importer et le tester.
Comme illustré au début de ce paragraphe, voici le résultat obtenu.
En tant que développeurs, ce résultat, fruit de dur labeur, peut nous satisfaire ; cependant, d'un point de vue du client final, notre application, en l'état aura une durée de vie limitée.
En effet, elle offre uniquement une vue statique de l'information.
L'utilisateur souhaitant prendre connaissance des différents départements de l'entreprise, utilisera une seule fois notre application. Elle ne présente finalement que peu d'intérêt pour l'utilisateur final, ceci en partie dû au manque d'interaction entre notre application et le public ciblé.
Nous allons donc voir, dans la partie suivante, comment la rendre plus agréable et plus fonctionnelle afin de correspondre au réel besoin du public ciblé.
V. Amélioration de notre application▲
La 1ère chose qu'il serait intéressant de modifier, ce sont tous les noms qui apparaissent dans notre application ; pour l'instant, nous avons utilisé des noms « internes » (AdventureWorksHumanResources, LobSystemWebInstance, etc..) ; ces noms ne sont pas très parlant pour nos utilisateurs, alors donnons leur quelque chose qui soit compréhensible pour eux.
Vous me direz « c'est du détail », mais nos utilisateurs finaux savent parfois être si difficiles.
V-A. Modification des libellés.▲
Grossièrement, pour modifier nos libellés, il va falloir rajouter un attribut DefaultDisplayName la ou nous avons déjà rajouté un attribut Name.
Nous allons donc faire les choses d'en l'ordre.
Remplaçons ceci (au niveau des services partagés)
Par ceci
Grâce à cette ligne.
<LobSystem
xmlns
=
"http://schemas.microsoft.com/office/2006/03/BusinessDataCatalog"
Type
=
"Database"
Version
=
"1.0.0.3"
Name
=
"AdventureWorksHumanResources"
DefaultDisplayName
=
"BDC - Application Ressources Humaines"
>
Puis (au niveau du data type picker) remplaçons ceci
Par ceci
Respectivement grâce à ces lignes
<LobSystemInstance
Name
=
"AdventureWorksHumanResourcesInstance"
DefaultDisplayName
=
"Application RH - Instance Web"
>
Puis
<Entity
Name
=
"Department"
DefaultDisplayName
=
"Départements de l'entreprise"
>
Au niveau de nos champs, Ceci
Par ceci
En modifiant ces lignes
<TypeDescriptor
TypeName
=
"System.Int16"
Name
=
"DepartmentID"
DefaultDisplayName
=
"ID"
/>
<TypeDescriptor
TypeName
=
"System.String"
Name
=
"Service"
DefaultDisplayName
=
"Service"
/>
<TypeDescriptor
TypeName
=
"System.String"
Name
=
"Department"
DefaultDisplayName
=
"Département"
/>
<TypeDescriptor
TypeName
=
"System.DateTime"
Name
=
"ModifiedDate"
DefaultDisplayName
=
"Date de dernière modification"
/>
Tant que nous y sommes, nous pourrions penser à masquer également des colonnes comme ID et Date de dernière modification car ces colonnes ne sont pas directement utiles à nos utilisateurs finaux qu'est le personnel du service RH.
V-B. Masquer des colonnes ▲
Pour masquer des colonnes, nous pouvons passer par la case configuration de notre Business Data List.
Pour cela, passons la page en mode édition ; un lien view apparait maintenant à droite de notre webpart.
Cliquons dessus, puis sur la page Edit View, dans la section columns, décochons ID et Date de dernière modification
Une fois ceci validé, nous obtenons un affichage plus sobre pour nos utilisateurs.
Une autre fonctionnalité, qui dans notre situation actuelle présente peu d'intérêt, mais ultérieurement va nous être utile dans le cadre de la récupération des employés, va être par exemple d'afficher dans une Business Data List toute la liste des employés en affichant par exemple uniquement leur nom et prénom, mais ensuite dans un Business Data Item, afficher plus d'information sur un seul employé.
Pour cela, il va nous falloir rajouter la possibilité de récupérer également 1 employé ; cette opération va être possible grâce à une methodInstance de type SpecificFinder.
V-C. Récupérer uniquement 1 département parmi toute une liste.▲
Comme signalé ci-dessus, pour récupérer 1 département, et surtout pouvoir l'afficher dans le business data item, il va nous falloir utiliser une methodInstance de type SpecificFinder.
Une méthode de type va avoir besoin d'une clé pour s'exécuter ; ici, notre clé sera le champ ID.
Grâce à une méthode de type SpecificFinder nous allons pouvoir exécuter une méthode du genre
--sql.adworks.sql
--Récupération d'un seul département; méthode instance SpecificFinder.
select
DepartmentID, [Name]
as
[Service]
, GroupName as
Department, ModifiedDate
from
HumanResources.Department
where
DepartmentID like
@ID
Tout à l'heure, nous avons parlé de la différence entre une méthode et une méthode instance ; une méthode est une sorte de signature, tandis qu'une méthode instance va être une implémentation de cette signature.
De plus, une méthode peut posséder plusieurs méthodes instances.
Ici, nous avons déjà une méthode GetDepartments; plutôt que d'en créer une 2ème, adaptons plutôt notre méthode existante pour afin de l'adapter à nos 2 besoins, la récupération de tous nos départements, et la récupération d'un département spécifique en fonction de son ID.
Commençons par adapter notre requête SQL ; la requête suivante vous nous permettre d'atteindre notre objectif.
--sql.adworks.sql
-- Récupération à la fois de tous les départements, mais aussi d'un seul.
select
DepartmentID, [Name]
as
[Service]
, GroupName as
Department, ModifiedDate
from
HumanResources.Department
where
(
DepartmentID >=
@MinDepartmentID)
AND
(
DepartmentID <=
@MaxDepartmentID)
Nous savons ici que nous avons 16 départements, il nous suffit donc de fixer respectivement les valeurs de @MinDepartmentID et de @MaxDepartmentID à un nombre inférieur à 0 et un nombre supérieur à 16 pour récupérer la totalité de nos départements ; de même, en fixant la valeur de @MinDepartmentID et de @MaxDepartmentID au même nombre, nous récupérons le département portant cet identifiant.
Encore reste-t-il à pouvoir passer en paramètre l'identifiant. Tiens, paramètre, on en a déjà parlé tout à l'heure lorsque nous avons récupéré nos données ; pour réaliser cela, nous avons créé un paramètre de direction Return; et bien pour cela, nous allons créer 2 paramètres de direction In; ces paramètres seront donc utilisés par SharePoint pour passer la valeur (l'identifiant) de notre département à la requête SQL.
<Parameter
Direction
=
"In"
Name
=
"@MinDepartmentID"
>
<TypeDescriptor
TypeName
=
"System.Int16"
IdentifierName
=
"DepartmentID"
AssociatedFilter
=
"ID"
Name
=
"MinDepartmentID"
>
<DefaultValues>
<DefaultValue
MethodInstanceName
=
"DepartmentFinderInstance"
Type
=
"System.Int16"
>
0</DefaultValue>
</DefaultValues>
</TypeDescriptor>
</Parameter>
<Parameter
Direction
=
"In"
Name
=
"@MaxDepartmentID"
>
<TypeDescriptor
TypeName
=
"System.Int16"
IdentifierName
=
"DepartmentID"
AssociatedFilter
=
"ID"
Name
=
"MaxDepartmentID"
>
<DefaultValues>
<DefaultValue
MethodInstanceName
=
"DepartmentFinderInstance"
Type
=
"System.Int16"
>
999</DefaultValue>
</DefaultValues>
</TypeDescriptor>
</Parameter>
Ici, nous fixons également les valeurs par défaut pour notre méthode DepartmentFinderInstance.
De cette façon, lorsque la méthode Finder sera appelée pour récupérer tous nos départements, le code suivant sera exécuté.
--sql.adworks.sql
-- Méthode FinderInstance
select
DepartmentID, [Name]
as
[Service]
, GroupName as
Department, ModifiedDate
from
HumanResources.Department
where
(
DepartmentID >=
0
)
AND
(
DepartmentID <=
999
)
Ce qui nous permet effectivement de récupérer tous les départements.
Nous pouvons d'ailleurs facilement le vérifier en utilisant l'outil SQL Server Profiler, qui va nous servir à intercepter toutes les requêtes qui arrivent directement sur notre serveur SQL. (Pour le démarrer, dans management Studio, cliquez sur Tools, puis SQL Server Profiler ; ensuite choisissez Nouvelle Trace, puis dans l'onglet Event Selection, sélectionnez uniquement StoredProcedures et TSQL).
De même, si nous essayons de récupérer le service principal du département R&D,
Nous remarquons que les 2 paramètres, prennent la même valeur, ce qui nous permet de récupérer un seul département.
Enfin, pour clôturer le tout et pouvoir utiliser notre Business Data Item de manière indépendante, il va nous falloir mettre en ouvre un filtre.
Voici donc le code notre filtre.
<FilterDescriptor
Type
=
"Comparison"
Name
=
"ID"
>
<Properties>
<Property
Name
=
"Comparator"
Type
=
"System.String"
>
Equals</Property>
</Properties>
</FilterDescriptor>
De plus, nous avons à présent la possibilité de spécifier la façon dont nous voulons que nos requêtes s'exécutent depuis la page Edit View ; nous pouvons utiliser le filtre ainsi créé pour filtrer nos recherches.
Vous voyez cependant que nous ne pouvons pas (encore) limiter le nombre de résultats retournés par notre requête ; même si plus bas sur la page Edit View, nous avons la possibilité, tout comme avec des listes SharePoint, de limiter le nombre de résultat affichés à l'écran,
Si notre requête doit nous retourner des millions de lignes, nous devrons attendre que la requête finisse de s'exécuter, ce qui peut prendre beaucoup de temps.
La prochaine étape va donc être de voir comment limiter le nombre de résultats retournés par notre requête.
V-D. Limiter le nombre de résultats retournés par une requête▲
A l'image de la récupération d'un seul élément qui est passée par la création d'un filtre et d'un paramètre, nous allons en faire de même pour limiter le nombre d'éléments.
Commençons par créer notre paramètre
<Parameter
Direction
=
"In"
Name
=
"@Limit"
>
<TypeDescriptor
TypeName
=
"System.Int32"
AssociatedFilter
=
"Limit"
Name
=
"Limit"
>
<DefaultValues>
<DefaultValue
MethodInstanceName
=
"DepartmentFinderInstance"
Type
=
"System.Int32"
>
9999</DefaultValue>
<DefaultValue
MethodInstanceName
=
"DepartmentSpecificFinderInstance"
Type
=
"System.Int32"
>
9999</DefaultValue>
</DefaultValues>
</TypeDescriptor>
</Parameter>
Par défaut, afin de récupérer tous nos éléments, nos fixons la limite à 9999 éléments, ce qui devrait nous permettre de récupérer nos 16 services et nous laisse une bonne marge de manouvre.
Créons ensuite le filtre correspondant
<FilterDescriptor
Type
=
"Limit"
Name
=
"Limit"
/>
Et pour terminer, modifions notre requête en y rajoutant une clause top.
select
top(
@Limit
)
DepartmentID, [Name]
as
[Service]
, GroupName as
Department, ModifiedDate
from
HumanResources.Department
where
(
DepartmentID >=
@MinDepartmentID)
AND
(
DepartmentID <=
@MaxDepartmentID)
Dorénavant, vous pouvez modifier le paramètre.
Et depuis le profiler, voici la requête que l'on voit passer.
Et si nous ne spécifions pas de filtre,
Il pourrait également être intéressant de donner à l'utilisateur la possibilité de filtrer ses éléments.
Pour cela, sur la page Edit View, il nous suffit de configurer le composant pour autoriser les filtres.
Pour l'instant, nous n'avons autorisé uniquement un filtre de type equals sur les identifiants, ce qui ne présente que peu d'intérêt du point de vue de l'utilisateur ; nous allons à présent rajouter un filtre qui va nous permettre de récupérer les départements & services par leur nom.
V-E. Récupérer des éléments grâce aux filtres.▲
Nous allons à présent voir comment autoriser nos utilisateurs à effectuer des requêtes en y précisant des filtres.
Pour cela, nous allons tout d'abord commencer par modifier notre requête SQL
--sql.adworks.sql
-- Ajout du filtre pour récupérer les départements et services.
select
top(
@Limit
)
DepartmentID, [Name]
as
[Service]
, GroupName as
Department, ModifiedDate
from
HumanResources.Department
where
(
DepartmentID >=
@MinDepartmentID)
AND
(
DepartmentID <=
@MaxDepartmentID)
and
(
[Name]
Like
@Service)
and
(
GroupName Like
@Department)
Puis, nous allons rajouter un filterdescriptor de type wildcard,
<FilterDescriptor
Type
=
"Wildcard"
Name
=
"Service"
/>
<FilterDescriptor
Type
=
"Wildcard"
Name
=
"Department"
/>
Leur attacher des paramètres, afin de les récupérer pour exécuter notre requête.
<Parameter
Direction
=
"In"
Name
=
"@Service"
>
<TypeDescriptor
TypeName
=
"System.String"
AssociatedFilter
=
"Service"
Name
=
"Service"
>
<DefaultValues>
<DefaultValue
MethodInstanceName
=
"DepartmentFinderInstance"
Type
=
"System.String"
>
%</DefaultValue>
<DefaultValue
MethodInstanceName
=
"DepartmentSpecificFinderInstance"
Type
=
"System.String"
>
%</DefaultValue>
</DefaultValues>
</TypeDescriptor>
</Parameter>
<Parameter
Direction
=
"In"
Name
=
"@Department"
>
<TypeDescriptor
TypeName
=
"System.String"
AssociatedFilter
=
"Department"
Name
=
"Department"
>
<DefaultValues>
<DefaultValue
MethodInstanceName
=
"DepartmentFinderInstance"
Type
=
"System.String"
>
%</DefaultValue>
<DefaultValue
MethodInstanceName
=
"DepartmentSpecificFinderInstance"
Type
=
"System.String"
>
%</DefaultValue>
</DefaultValues>
</TypeDescriptor>
</Parameter>
Et enfin pour terminer, nous allons spécifier quel est le caractère joker de notre LobSystem. Pour SQL Server, il s'agit de « % », nous allons donc le spécifier dans les propriétés de notre LobSystemInstance
<LobSystem
xmlns
=
"http://schemas.microsoft.com/office/2006/03/BusinessDataCatalog"
Type
=
"Database"
Version
=
"1.0.0.19"
Name
=
"AdventureWorksHumanResources"
DefaultDisplayName
=
"BDC - Application Ressources Humaines"
>
<Properties>
<Property
Name
=
"WildcardCharacter"
Type
=
"System.String"
>
%</Property>
</Properties>
Si nous regardons la requête qui passe, nous pouvons nous rendre compte de l'utilisation qui en est fait.
Pour sélectionner un item, actuellement nous sommes obligés de renseigner son identifiant, ce qui n'est pas forcément une chose aisée.
Il serait par exemple plus intéressant pour eux de pouvoir sélectionner directement le service qui les intéresse.
Voyons donc comment réaliser cette opération.
V-F. Afficher des propriétés dans le data item picker▲
Pour rajouter des propriétés au data item picker, il nous suffit de renseigner une propriété au niveau de notre entité.
<Entity
Name
=
"Department"
DefaultDisplayName
=
"Départements de l'entreprise"
>
<Properties>
<Property
Name
=
"Title"
Type
=
"System.String"
>
Service</Property>
</Properties>
Ceci nous suffit à afficher le résultat vu au précédent paragraphe.
Cependant, il se peut que nous ayons besoin d'afficher plusieurs propriétés ; pour cela, au niveau du type descriptor, il nous suffit de mettre à true la propriété ShowInPicker.
<TypeDescriptor
TypeName
=
"System.String"
Name
=
"Department"
DefaultDisplayName
=
"Département"
>
<Properties>
<Property
Type
=
"System.Boolean"
Name
=
"ShowInPicker"
>
true</Property>
</Properties>
</TypeDescriptor>
Et voici le résultat.
De même, dorénavant, en modifiant le title dans la page edit view, il nous est possible d'offrir un menu ECB à nos utilisateurs afin qu'ils puissent exécuter les actions que nous leurs mettrons à disposition.
Voici donc le résultat.
Ceci clôt le sujet sur la customisation de nos entités.
A présent, nous avons donc une application qui nous permet de récupérer la liste de nos départements, et d'en afficher le détail, soit via une action, soit en utilisant les connexions de webparts. Nous avons également mis en place un certain nombre de paramètres qui nous permettre de mettre à disposition de nos utilisateurs une application réactive et « userfriendly ».
Nous allons à présent rajouter l'entité Employés.
Cette étape ne sera pas détaillée dans cet article, car cela revient grossièrement à refaire ce que nous avons déjà fait ici pour l'entité département ; néanmoins, je vous invite à regarder le fichier adf joint pour voir quels sont les quelques changements mineurs (comme par exemple l'utilisation d'une procédure stockée en lieu et place d'une requête texte).
Voici l'état de notre application après avoir ajouté cette entité :
En affichant le profil de notre collaborateur, nous obtenons plus de détail.
La prochaine partie de cette article va donc traiter des associations ; nous allons voir comment les mettre en ouvre pour rajouter de l'interactivité entre nos différents composants.
Remarque : Certains paramètres, propriétés , methodInstances , etc. . n'ont pas été mise en ouvre ici car ces dernières ne sont pas utilisables par les composants OOTB du BDC fournis et nécessite nt de passer par du développement ; je pense notamment à l'utilisation de methodInstances de type GenericInvoker qui vont nous permettre d'ajouter/modifier/supprimer des données directement dans notre backend . Nous les verrons dans un article ultérieur traitant de l'utilisation du modèle objet du BDC.
V-G. Récupération de nos employés à partir de nos départements.▲
Nous allons ici définir une association entre notre entité département et notre entité collaborateurs, afin de pouvoir récupérer nos collaborateurs en fonction de nos départements.
Pour cela, dans une de nos entités, il va falloir définir une nouvelle méthode
Voici le code de la méthode que nous avons défini pour implémenter notre association ; cette méthode a été rajoutée à l'entité Department.
<Method
Name
=
"GetEmployeesByDept"
>
<Properties>
<Property
Name
=
"RdbCommandText"
Type
=
"System.String"
>
uspGetEmployeesByDept
</Property>
<Property
Name
=
"RdbCommandType"
Type
=
"System.Data.CommandType"
>
StoredProcedure
</Property>
</Properties>
<Parameters>
<Parameter
Direction
=
"In"
Name
=
"@ID"
>
<TypeDescriptor
TypeName
=
"System.Int16"
IdentifierName
=
"DepartmentID"
Name
=
"ID"
/>
</Parameter>
<Parameter
Direction
=
"Return"
Name
=
"Employees"
>
<TypeDescriptor
TypeName
=
"System.Data.IDataReader, System.Data,
Version=2.0.3600.0, Culture=neutral,
PublicKeyToken=b77a5c561934e089"
IsCollection
=
"true"
Name
=
"EmployeeDataReader"
>
<TypeDescriptors>
<TypeDescriptor
TypeName
=
"System.Data.IDataRecord, System.Data,
Version=2.0.3600.0, Culture=neutral,
PublicKeyToken=b77a5c561934e089"
Name
=
"EmployeeDataRecord"
>
<TypeDescriptors>
<TypeDescriptor
TypeName
=
"System.Int32"
IdentifierEntityName
=
"Employee"
IdentifierName
=
"EmployeeID"
Name
=
"EmployeeID"
DefaultDisplayName
=
"ID"
/>
<TypeDescriptor
TypeName
=
"System.String"
Name
=
"Secu"
DefaultDisplayName
=
"Securité Sociale"
/>
<TypeDescriptor
TypeName
=
"System.String"
Name
=
"LoginID"
DefaultDisplayName
=
"Login"
/>
<TypeDescriptor
TypeName
=
"System.String"
Name
=
"Name"
DefaultDisplayName
=
"Nom"
/>
<TypeDescriptor
TypeName
=
"System.String"
Name
=
"Phone"
DefaultDisplayName
=
"Téléphone"
/>
<TypeDescriptor
TypeName
=
"System.String"
Name
=
"Mail"
DefaultDisplayName
=
"Mail"
/>
<TypeDescriptor
TypeName
=
"System.String"
Name
=
"Manager"
DefaultDisplayName
=
"Responsable"
/>
<TypeDescriptor
TypeName
=
"System.String"
Name
=
"Title"
DefaultDisplayName
=
"Fonction"
/>
<TypeDescriptor
TypeName
=
"System.String"
Name
=
"Service"
DefaultDisplayName
=
"Service"
/>
<TypeDescriptor
TypeName
=
"System.String"
Name
=
"Department"
DefaultDisplayName
=
"Département"
/>
<TypeDescriptor
TypeName
=
"System.DateTime"
Name
=
"BirthDate"
DefaultDisplayName
=
"Date de Naissance"
/>
<TypeDescriptor
TypeName
=
"System.DateTime"
Name
=
"HireDate"
DefaultDisplayName
=
"Date d'embauche"
/>
<TypeDescriptor
TypeName
=
"System.String"
Name
=
"Shift"
DefaultDisplayName
=
"les 3 huits"
/>
<TypeDescriptor
TypeName
=
"System.Int16"
Name
=
"VacationHours"
DefaultDisplayName
=
"Heures de vacances"
/>
<TypeDescriptor
TypeName
=
"System.Int16"
Name
=
"SickLeaveHours"
DefaultDisplayName
=
"Heures de congé de maladie"
/>
<TypeDescriptor
TypeName
=
"System.DateTime"
Name
=
"ModifiedDate"
DefaultDisplayName
=
"Date de dernière modification"
/>
</TypeDescriptors>
</TypeDescriptor>
</TypeDescriptors>
</TypeDescriptor>
</Parameter>
</Parameters>
</Method>
Ici nous définissons de manière classique notre méthode, comme nous l'avons fait jusqu'à présent ; nous définissons notre requête SQL à exécuter (ici une procédure stockée), puis nos paramètres d'entrée et de sortie ; nous aurions également pu définir des filtres, ca fonctionne de la même façon que ce que nous avons déjà réalisé.
Un point important à noter ici, est que dans notre paramètre de retour, nous retournons une autre entité, nos employés ; pour préciser cela, nous avons utilisé l'attribut IdentifierEntityName="Employee" lorsque nous avons défini l'identifiant de notre entité dans notre type de retour.
La seule différence à noter ici par rapport à une méthode « classique » est le fait que nous n'ayons pas besoin de définir de MethodInstance; en effet, la requête est exécutée lors de la sélection d'un item.
Ensuite, il ne nous reste plus qu'à définir notre association, juste en dessous de la balise </Entities>.
<Associations>
<Association
AssociationMethodEntityName
=
"Department"
AssociationMethodName
=
"GetEmployeesByDept"
AssociationMethodReturnParameterName
=
"Employees"
Name
=
"GetEmployeesByDepartement"
>
<SourceEntity
Name
=
"Department"
/>
<DestinationEntity
Name
=
"Employee"
/>
</Association>
</Associations>
V-H. Ajout de quelques actions▲
Les actions vont permettre à nos utilisateurs de réaliser des opérations supplémentaires sur nos données, ou autre, directement depuis l'interface graphique.
Il s'agit de simple url, qui peuvent avoir une portée locale, directement sur l'item, comme ci-dessous,
Et/ou une portée globale, et donc s'appliquer à toute la liste.
Voici le code utilisé pour définir chacune des actions Export To Excel et Send Mail.
<Actions>
<Action
Name
=
"ExportToExcel"
DefaultDisplayName
=
"Export To Excel"
IsOpenedInNewWindow
=
"false"
ImageUrl
=
"/_layouts/images/icxlsx.gif"
Url
=
"/_layouts/BDCexpToXlsx.aspx"
Position
=
"3"
/>
<Action
Name
=
"SendMail"
DefaultDisplayName
=
"Send Mail"
IsOpenedInNewWindow
=
"false"
Position
=
"1"
Url
=
"mailto:{0}"
ImageUrl
=
"/_layouts/1033/images/sendmail.gif"
>
<ActionParameters>
<ActionParameter
Index
=
"0"
Name
=
"Mail"
/>
</ActionParameters>
</Action>
</Actions>
Ce bout de code est à placer dans l'entitéEmployee, sous la balise </Methods>.
Comme vous pouvez le constater, dans l'action accessible au niveau de l'item, nous avons précisé un paramètre en querystring '{0}' ; ce paramètre est ensuite relié au ActionParameter via la propriété Index, puis à un type descriptor correspondant à un des typeDescriptor du typeDescriptor Return de la méthode contenant l'instance de méthode specificfinder ; dans notre cas, il s'agit du mail.
<Method
Name
=
"GetEmployees"
>
<Parameter
Direction
=
"Return"
Name
=
"Employees"
>
<TypeDescriptor
TypeName
=
"System.String"
Name
=
"Mail"
DefaultDisplayName
=
"Mail"
/>
<MethodInstances>
<MethodInstance
Name
=
"EmployeeSpecificFinderInstance"
Type
=
"SpecificFinder"
ReturnParameterName
=
"Employees"
/>
</MethodInstances>
</Method>
Comme vous avez pu le constater, il est très facile d'intégrer nos propres actions dans nos fichiers ADF.
A présent, il nous reste un dernier point à voir, les outils de génération de fichier ADF.
VI. Les outils de génération de fichier ADF.▲
Actuellement, il existe 2 outils de génération de fichier ADF, l'un fourni par Microsoft avec le SDK.
Vous avez un certain nombre d'exemple en ligne sur le site de la MSDN, comme par exemple celui-ci > http://msdn.microsoft.com/en-us/library/bb736296.aspx pour une base de données.
Le 2ème outil est le BDC Meta Man, que je n'ai pu tester que dans sa version developer.
Il est cependant à noter que la version développeur du BDC Metaman connait beaucoup (trop ??)de limite.
Ces outils sont surtout intéressants dans la mesure où ils vont nous éviter d'écrire du code à la main et donc de gagner du temps ; cependant, si vous souhaitez utiliser au maximum les capacités du BDC, ou du moins écrire un certain nombre de scénarios avancés, interagissant avec les composant OOTB de SharePoint, il peut être intéressant de générer un fichier ADF 'de base' grâce à la puissance de ces outils, et ensuite d'éditer manuellement ce fichier afin d'y incorporer les fonctionnalités avancées que vous souhaitez mettre en ouvre.
VII. Points supplémentaires▲
VII-A. Les différentes méthodes de connexion▲
Jusqu'à présent, lorsque nous avons défini les méthodes de connexion à notre entité, nous avons à chaque fois configuré notre mode d'authentification à PassThrough.
<Property
Name
=
"AuthenticationMode"
Type
=
"Microsoft.Office.Server.ApplicationRegistry.SystemSpecific.Db.DbAuthenticationMode"
>
PassThrough
</Property>
Ce mode d'authentification nous permet de récupérer les identifiants de l'utilisateur actuel et de les utiliser pour authentifier notre utilisateur. De cette façon, seuls les utilisateurs autorisés pourront utiliser notre application pour récupérer les données.
Ce mode d'authentification fonctionne très bien . à condition d'avoir tous les services installés sur le même serveur. Dans notre cas par exemple, le serveur SQL auquel nous accédons est installé sur la même machine, et donc si l'utilisateur authentifié est autorisé à accéder au serveur SQL, il pourrait récupérer les données, sinon, il recevra un message d'erreur en essayant d'exécuter la requête.
Si cependant, notre serveur SQL était sur un serveur différent, à ce moment, en utilisant la méthode d'authentification Passthrough, on se retrouve confronté à la problématique du double hop : les informations d'authentification ne peuvent pas être transmises de serveur en serveur.
De meme, si le système auquel nous souhaitons accéder ne supporte pas l'authentification windows, notre méthode Passthrough ne présente pas non plus dans ce cas de figure plus d'intérêt.
Suite à la réflexion autour des 2 scénarios précédents, il convient de trouver une alternative.
Pour résoudre les problématiques énoncées ci-dessus, SharePoint nous propose une solution, qui est la possibilité d'utilisé le service Single Sign On (SSO) ou authentification unique.
Le service SSO de SharePoint, va nous permettre de nous authentifier une seule fois et ensuite de pouvoir accéder aux différents services sur différents serveurs, en étant directement authentifié, et résolvant par la même occasion les problèmes de double hop.
Le principe est très simple ; une fois authentifié, le service SSO va stocker les informations de connexion de l'utilisateur dans une base de données (de manière cryptée) ; puis à chaque fois que notre utilisateur essayera de se connecter, SSO ira vérifier si les informations de l'utilisateur sont présentes dans la base, et si oui, il les utilisera pour authentifier l'utilisateur. Si elles ne sont pas présentes, alors il s'agit de la 1ère authentification de l'utilisateur.
Par mi les modes d'authentification du BDC, nous allons pouvoir en utiliser 2 qui vont nous permettre de résoudre notre problématique énoncée plus haut, à savoir :
- Comment utiliser le BDC et l'authentification Windows pour autoriser nos utilisateurs à accéder à une application qui se trouve sur un serveur distant ?
- Comment utiliser le BDC et une authentification Login/mot de passe quelconque pour autoriser nos utilisateurs à accéder à une application (distante ou pas)?
Pour répondre à la 1ère question, nous allons pouvoir utiliser le mode d'authentification WindowsCredentials.
<Properties>
<Property
Name
=
"AuthenticationMode"
Type
=
"Microsoft.Office.Server.ApplicationRegistry.SystemSpecific.Db.DbAuthenticationMode"
>
WindowsCredentials</Property>
<Property
Name
=
"SsoApplicationId"
Type
=
"System.String"
>
AdventureWorks</Property>
<Property
Name
=
"SsoProviderImplementation"
Type
=
"System.String"
>
Microsoft.SharePoint.Portal.SingleSignon.SpsSsoProvider,Microsoft.SharePoint.Portal.SingleSignon,Version=12.0.0.0,
Culture=neutral,PublicKeyToken=71e9bce111e9429c
</Property>
<Property
Name
=
"DatabaseAccessProvider"
Type
=
"Microsoft.Office.Server.ApplicationRegistry.SystemSpecific.Db.DbAccessProvider"
>
SqlServer
</Property>
<Property
Name
=
"RdbConnection Data Source"
Type
=
"System.String"
>
.</Property>
<Property
Name
=
"RdbConnection Initial Catalog"
Type
=
"System.String"
>
AdventureWorks</Property>
<Property
Name
=
"RdbConnection Integrated Security"
Type
=
"System.String"
>
SSPI</Property>
</Properties>
Et configurer le SSO pour utiliser l'authenfication Windows.
Pour configurer le SSO, le service Microsoft Sigle Sign-On doit être démarré sur le serveur Sharepoint adéquate ; puis, toujours depuis ce serveur, rendez vous sur la page Opération du site d'administration centrale, puis dans la section choisissez Manage Settings for single sign-on.
Sélectionnez ensuite Manage Server Settings for SSo, et configurez cette page de manière adéquate.
Ici nous sommes sur un serveur de test ; cepedant, je vous conseille ce lien pour configurer cette page en respectant les best practices.
Une fois validé, nous allons à présent configurer le SSO pour notre application, en sélectionnant « Manage Settings for enterprise application definitions », puis selectionner un nouvel élément.
Ici, nous allons donner le nom de l'item, une adresse de contact, et surtout, le nom que nous allons ensuite utiliser pour identifer notre application. Ce nom sera également à préciser dans notre fichier ADF ; de cette facon, Sharepoint saura faire la relation entre les 2 !
<Property
Name
=
"SsoApplicationId"
Type
=
"System.String"
>
AdventureWorks</Property>
Puis, nous allons créer un compte générique, qui sera ensuite utilisé par tout le monde ; pour cela, nous allons sélectionner Group dans la section Account Type,
Et nous allons cocher la case Windows authentication dans le cas d'une authentification Windows ; dans le cas d'une authentification par login/mot de passe, nous n'aurions pas coché cette case ; c'est la seule différence qui existe entre les 2 modes au niveau du SSO ; au niveau du BDC, nous aurions tout simplement remplacer WindowsCredentials par RdbCredentials (pour un LobSytem de type base de données, Credentials si le type est web service) dans la propriété Authentication Mode.
Pour terminer avec la configuration de cette page, nous allons spécifier que nous allons renseigner un nom d'utilisateur et un mot de passe, en utilisant des masques pour ne pas afficher le mot de passe lorsque l'utilisateur le saisira.
Une fois cette page configurée, il ne nous reste plus qu'à préciser quel est le compte qui sera utilisé, et par qui.
Pour cela, depuis la page Manage settings for SSO, cliquons sur Manage account information for enterprise application definition
Choisissons tout d'abord quel item l'on va utiliser, ici, nous allons reprendre l'item créé précédemment, à savoir Generic User, puis, il faut à présent dire à quel groupe nous souhaitons le mapper ; ici, nous avons mappé notre « Generic user à tous les utilisateurs du domaine ; une fois cette opération effectuée, avant de valider, il va nous falloir définir le compte représenté par notre « Generic user » ; pour cela, il faut choisir Set, et non pas Done.
Ici, nous avons spécifié que notre « Generic User » devait utiliser les informations de compte de l'administrateur (nous sommes sur une machine de test !).
En validant, dorénavant, les opérations réalisées par les utilisateurs de nos applications BDC, portant l'identifiant AdventureWorks, et utilisant l'authentification WindowsCredentials, seront considérés par notre système comme étant des opérations réalisées par l'administrateur ! et donc meme des personnes n'ayant pas d'accès à SQL Server peuvent à présent voir les données qui y sont stockées. De plus, désormais, nous pouvons accéder à nos système distant ! Fini la problématique de double hop !
Nous aurions également pu utiliser l'authentification SQL Server pour accéder à notre système ; à ce moment, il aurait suffi de ne pas cocher Windows authentification.
Il existe un dernier mode d'authentification, qui est d'ailleurs celui appliqué par défaut par Sharepoint, si la propriété AuthenticationMode était manquante, il s'agit de RevertToSelf.
<Property
Name
=
"AuthenticationMode"
Type
=
"Microsoft.Office.Server.ApplicationRegistry.SystemSpecific.Db.DbAuthenticationMode"
>
RevertToSelf</Property>
Cette propriété va nous permettre d'emprunter l'identité du pool d'application de l'application sur laquelle on se situe ; encore faut-il que ce compte aie accès aux diverses ressources auxquelles nous nous connections via nos applications BDC.
VII-B. Travailler avec plusieurs LobSystemIntances▲
Nous avons précisé au début de cet article que le lobsystemIntance allait être utilisé pour passer nos information de connexion à notre système.
Au sein de notre application, nous allons pouvoir renseigner, et donc travailler avec des lobsysteminstances différents ,ce qui va nous permettre de passer des informations de connexion différentes, ou de se connecter à des serveurs différents.
Il existe cependant une contrainte très forte au niveau de nos différents LobSytemInstances :
- Il faut absolument qu'ils aient la meme structure, de sorte que les entités modélisées par la suite apparaissent exactement de la meme facon dans chacune de nos instances.
Une fois cette contrainte respectée, nous pouvons donc passer plusieurs informations de connexions, via plusieurs LobSystemInstances.
<LobSystemInstance
Name
=
"HRWindowsCredentials"
DefaultDisplayName
=
"Application RH - Authentification Windows via SSO"
>
<Properties>
<Property
Name
=
"AuthenticationMode"
Type
=
"System.String"
>
WindowsCredentials</Property>
<Property
Name
=
"SsoApplicationId"
Type
=
"System.String"
>
AdventureWorks</Property>
<Property
Name
=
"SsoProviderImplementation"
Type
=
"System.String"
>
Microsoft.SharePoint.Portal.SingleSignon.SpsSsoProvider,Microsoft.SharePoint.Portal.SingleSignon,
Version=12.0.0.0, Culture=neutral,PublicKeyToken=71e9bce111e9429c
</Property>
<Property
Name
=
"DatabaseAccessProvider"
Type
=
"Microsoft.Office.Server.ApplicationRegistry.SystemSpecific.Db.DbAccessProvider"
>
SqlServer
</Property>
<Property
Name
=
"RdbConnection Data Source"
Type
=
"System.String"
>
.</Property>
<Property
Name
=
"RdbConnection Initial Catalog"
Type
=
"System.String"
>
AdventureWorks</Property>
<Property
Name
=
"RdbConnection Integrated Security"
Type
=
"System.String"
>
SSPI</Property>
</Properties>
</LobSystemInstance>
<LobSystemInstance
Name
=
"HRCredentials"
DefaultDisplayName
=
"Application RH - Authentification Login/Mot de Passe"
>
<Properties>
<Property
Name
=
"AuthenticationMode"
Type
=
"System.String"
>
RdbCredentials</Property>
<Property
Name
=
"SsoApplicationId"
Type
=
"System.String"
>
AdventureWorksSQL</Property>
<Property
Name
=
"SsoProviderImplementation"
Type
=
"System.String"
>
Microsoft.SharePoint.Portal.SingleSignon.SpsSsoProvider,Microsoft.SharePoint.Portal.SingleSignon,Version=12.0.0.0,
Culture=neutral,PublicKeyToken=71e9bce111e9429c
</Property>
<Property
Name
=
"DatabaseAccessProvider"
Type
=
"Microsoft.Office.Server.ApplicationRegistry.SystemSpecific.Db.DbAccessProvider"
>
SqlServer
</Property>
<Property
Name
=
"RdbConnection Data Source"
Type
=
"System.String"
>
.</Property>
<Property
Name
=
"RdbConnection Initial Catalog"
Type
=
"System.String"
>
AdventureWorks</Property>
<Property
Name
=
"RdbConnection Integrated Security"
Type
=
"System.String"
>
SSPI</Property>
</Properties>
</LobSystemInstance>
Il semblerait cependant que nous soyons limités à 2 instances par LobSystem ; en effet, lorsque nous essayons d'en ajouter plus, SharePoint nous remonte une erreur, tout d'abord au niveau de notre fichier adf,
puis au moment de l'import.
Une solution de contournement consisterait à cloner le fichier adf, et d'y modifier le nom, puis d'y ajouter des LobSytemInstances 2 par 2.
En utilisant, plusieurs LobSystemInstances, nous allons pouvoir utiliser notre application en poursuivant des objectifs différents ; par exemple, dans le cas de notre application, nous pourrions par exemple implémenter 2 LobSystemInstances, l'une récupérant les informations de compte de l'utilisateur (PassThrough), et permettant à nos utilisateurs de ne récupérer que les informations qu'il est autorisé à visualiser, et l'autre récupérant des informations d'un compte bases de données login/mot de passe (RdbCredentials), autorisé à accéder à toutes les données présentes dans notre base.
Par exemple, nos utilisateurs utiliseraient le 1er LobSytemInstance, tandis que l'on pourrait configurer l'indexation du BDC pour utiliser le 2nd LobSystemInstance.
De cette facon, nos utilisateurs récupèrent uniquement les informations qui leur sont destinés, tandis que la recherche récupèrera l'ensemble des informations.
Dans ce cas, on pourrait se dire qu'il y'a faille de sécurité dans la mesure ou finalement, nos utilisateurs pourraient donc passer par le service de recherche pour accéder à toutes les informations ; mais le BDC implémente aussi le mécanisme de security trimming (qu'il faudra explicitement mettre en place), ce qui signifie que malgré l'utilisation du service de recherche qui accès à tout le contenu, nos utilisateurs ne verront que les informations qu'ils sont autorisés à récupérer.
Si nous créons plusiseurs instances, elle n'apparaitrons pas au niveau des services partagés, qui nous montrent uniquement nos LobSystems ;
On les verra apparaitre par exemple au niveau du Business data type picker.
VIII. Conclusion▲
C'est ici que s'achève ce nouvel article traitant du BDC.
Dans ce 2ème volet, vous avez pu y découvrir quelques unes des fonctionnalités avancées du BDC, et surtout, vous avez désormais toutes les bases pour vos lancer dans l'écriture de vos propres fichiers ADF et la mise au point d'application utilisant les composants OOTB du BDC.
Dans le prochain article, nous allons voir comment créer un fichier ADF pour interroger des web services, et également l'écriture de fichiers ADF ressources.