SSIS Intégré à une architecture .net (2) : Utiliser une Web API en tant que source d’un Data-Flow

Dans cet article précédent, je vous proposais de faire converger l’implémentation d’une application .net orienté domaine et l’import de données via SSIS en réutilisant au sein d’un data-flow une assembly.net de définition et validation de ce domaine. Dans ce nouvel article je vous propose d’aller un pas en avant et de considérer que le système d’information, codé en .net, dispose d’un certain nombre de services (exposés par exemple via des Web API REST, mais il pourrait s’agir de service WCF ou de WebService). Mon package SSIS ici est chargé de l’export d’un référentiel à partir de données provenant d’une Web API « catalogue parution ».

Décrire un composant source pour Interroger la Web API


Je dispose donc d’une Web API qui expose une méthode GET, sans paramètre, destiné à retourner la liste des parutions de mon référentiel.
Le code de cette Web API est le suivant :

using System.Collections.Generic;
using System.Web.Http;
using ParutionDomain;

namespace ParutionWebApi.Controllers
{
    public class ParutionsController : ApiController
    {
        // GET api/values
        public List Get()
        {
            var res = new List()
                {
                    new Parution()
                        {
                            Codebarre = "03780010601004",
                            Codification = "0010",
                            Libelle = "L'Equipe",
                            Numero = 60
                        },
                    new Parution()
                        {
                            Codebarre = "04190191701506",
                            Codification = "1917",
                            Libelle = "Bravo",
                            Numero = 01
                        }
                };
            return res;
        }
    }
}

Les objets retournés sont des instances de la classe parution suivante toujours contenue dans mon assembly Domain :

namespace ParutionDomain
{
    public class Parution
    {
        public int Numero { get; set; }
        public string Libelle { get; set; }
        public string Codification { get; set; }

        public string Codebarre { get; set; }

        #region BusinessRules
        public bool IsValid()
        {
            return TestBarCodeValid();
        }

        private bool TestBarCodeValid()
        {
            string barCodeRule =string.Format("{0}{1}", Codebarre.Substring(0,5), Codification);

            return Codebarre.StartsWith(barCodeRule);
        }
        #endregion
    }
}

Pour l’utilisation depuis SSIS, j’héberge cette Web API sur le port 51.
Via mon navigateur Web, je peux interroger l’url http://localhost:51/Api/Parutions pour visualiser le Xml retourné.

L’implémentation dans SSIS


Aucune source n’étant disponible dans la toolbox SSIS pour interroger une Web API REST, mon reflexe est d’utiliser un composant de script.
Je vais désormais implémenter mon data-flow. Celui-ci doit débuter par un composant de script de type source. En déposant le composant de script, je vais pouvoir faire ce choix

Script source
Dans un composant Script source je dois :

1. Définir les colonnes

Je choisis de créer une colonne par propriété de mon objet parution et j’adapte les métadonnées de manière à ce qu’elles soient compatibles.
Définition des colonnes

2. Implémenter à minima la méthode CreateNewOutpuRows


C’est dans l’implémentation de cette méthode que je souhait interroger la Web API et créer un flux de données « parutions ».
En .net, l’interrogation d’une Web API peut se faire en exploitant les assemblies

  • System.Net.Http
  • System.Net.Http.Formatting

contenant les namespaces System.Net.Http et System.Net.Http.Headers.
L’assembly System.net.Http.Formatting provient d’une extension du framework à importer via nugget intitulée Web API Client. Comme pour toute assembly à utiliser dans SSIS, elle doit être mise dans le GAC (ce que Nugget package manager ne fait pas) ainsi que l’assembly NewtonSoft.json sur laquelle elle repose. Ces deux assemblies sont à trouver pour la première dans le dossier assemblies du dossier d’installation d’ASP.net MVC4 et pour la seconde dans le dossier package du dossier d’installation d’ASP.net MVC4.
Je vais également référencer la dll du domaine de manière à pouvoir caster les instances reçues de l’API en Parution. Comme je l’expliquais dans le premier article, cette assembly est fortement signée et déployée dans le GAC pour être référencée dans SSIS.
Ce qui me donne dans le composant script source de mon data-flow le code suivant :

using System;
using System.Collections.Generic;
using System.Data;
using System.Net.Http;
using System.Net.Http.Headers;
using Microsoft.SqlServer.Dts.Pipeline.Wrapper;
using Microsoft.SqlServer.Dts.Runtime.Wrapper;

[Microsoft.SqlServer.Dts.Pipeline.SSISScriptComponentEntryPointAttribute]
public class ScriptMain : UserComponent
{
   public override void CreateNewOutputRows()
    {
        HttpClient client = new HttpClient();
        client.BaseAddress = new Uri("http://localhost:51/");

        // Appel du Get de la Web API
        HttpResponseMessage response = client.GetAsync("api/parutions").Result;
        if (response.IsSuccessStatusCode)
        {
            var parutions = response.Content.ReadAsAsync<IEnumerable>().Result;
            foreach (var p in parutions)
            {
                Output0Buffer.AddRow();
                Output0Buffer.Codebarre = p.Codebarre;
                Output0Buffer.Codification = p.Codification;
                Output0Buffer.Libelle = p.Libelle;
                Output0Buffer.Numero = p.Numero
            }
        }
        else
        {
            throw new Exception(string.Format("{0} ({1})", (int)response.StatusCode, response.ReasonPhrase));
        }
    }
}

Je connecte ensuite une destination de type fichier plat reprenant toutes les colonnes de mon flux qui me sert de témoin.
Mon package s’exécute correctement en interrogeant la Web API :
Exécution du package

Utilisation d’un ConnectionManager


Je vais légèrement modifier mon composant Script pour m’appuyer sur un connection manager pour récupérer la chaine de connexion à la Web API. Je pourrai ainsi facilement exposer cette information en guise de paramètre du package ou du projet.
Je commence donc par définir un connection manager de type http connection manager que j’initialise avec l’url de ma Web API.
Je référence ensuite ce connection manager dans mon composant Script
Ajouter un connection manager au composant script
Puis je modifie mon code pour accéder à la collection de connection manager lorsque j’ai besoin de récupérer l’URL de base de la Web API.
Je remplace simplement cette ligne :


client.BaseAddress = new Uri("http://localhost:51/");

par :


client.BaseAddress = new Uri(Connections.WebApiConnection.ConnectionString);

L’exécution de mon package fonctionne désormais sur la base d’un connection manager.

Dans un prochain article, je vous proposerai de créer un composant custom Web API source pour proposer cette fonctionnalité directement dans la Toolbox.