UGISS Community

Il sito della community dello User Group Italiano di SQL Server
Welcome to UGISS Community Sign in | Join | Help
in Search

Alessandro Alpi's blog

Parliamo di integration services e non solo..

dicembre 2007 - Posts

  • [OT] - Auguri di Buone Feste

    Auguri di Buon Natale e Felice Anno nuovo a tutti!
    e come sempre..
    Stay Tuned!

  • [SSIS] - Il bypass del Web Service Task con uno script task

     Con SQL Server Integration Services è possibile utilizzare un particolare task, chiamato Web service Task, utilizzato proprio per invocare Web services. E' il seguente:


    Prendiamo un semplicissimo scenario, il web service di default che Visual Studio 2005 ci propone quando creiamo un progetto Web Service. Il classico Hello World.
    L'utilizzo del task è molto semplice. Gli step da seguire per poter usufruire delle sue funzionalità sono i seguenti:

    - Creare un connection Manager di tipo HTTP


    Nell'esempio ho impostato semplicemente il link http al mio WSDL di prova, ma come potete notare ci sono altre impostazioni molto importanti da poter utilizzare.
    Ad esempio è possibile fornire username, password e dominio di accesso, impostare un certificato, definire il timeout, la dimensione del blocco per la scrittura dei dati e testare la connessione.
    In alto vi è anche la clip dedicata alle impostazioni di un eventuale proxy.
    Siccome questa connessione HTTP è stata appositamente creata per un web service task e siccome avrò bisogno di conoscere la struttura del servizio, e quindi un WSDL, l'url di riferimento comprende il parametro ?WSDL. Chiamiamolo Servizio

    - Creare una variabile che conterrà la risposta (o un connection manager File per scrivere la risposta su filesystem)

    E' possibile ad esempio creare una variabile di tipo stringa che conterrà la risposta del nostro servizio.



    - Creare nella cartella del nostro progetto o su di una posizione nel disco ben definita un file vuoto con estensione .wsdl



    - Impostare il Web Service Task come segue:

    Fare doppio click sul Web service Task.
    Come prima cosa selezionare la http connection che abbiamo creato pocanzi e poi definire nella proprietà WSDLFile il percorso ove il file .wsdl è stato creato.
    La terza proprietà, OverwriteWSDLFile, indica se si vuole sovrascrivere il file alla pressione del tasto sottostante, il Download WSDL.
    La prima volta, ed ogni volta che la struttura del servizio cambia (e quindi ogni volta che si vuole scaricare il nuovo WSDL) lasciare questa proprietà impostata a True.
    A questo punto, premere il tasto Download WSDL e attendere il messaggio di conferma:



    Se la proprietà OverwriteWSDLFile è a False, viene restituito un messaggio di errore.
    Facoltativamente, potete andare a controllare sul disco e noterete che il file scaricato è effettivamente il WSDL del servizio che dobbiamo invocare.

    - Definire la sezione Input

    Spostarsi sulla sezione input. Scegliere il servizio (proprietà Service) ed il metodo da eseguire (proprietà Method). Nel caso in cui il metodo abbia parametri, comparirà un'apposita sezione al di sotto della selezione della chiamata. Qui di seguito un esempio di chiamata con un parametro stringa (metodo Message), mentre quello al precedente punto non ne ha alcuno:



    Attenzione, perchè per i tipi che non sono considerati primitivi, come possono esserlo interi e stringhe, SSIS non è in grado di fornire il passaggio di parametri. Ad esempio, un metodo definito come il seguente, non restituirà a SSIS nessuna richiesta di parametri input:

    <WebMethod()> _
        Public Function NumeroRighe(ByVal ds As DataSet) As Integer
           
    Return ds.Tables(0).Rows.Count
        End Function


    - Definire la sezione Output

    Dopo aver configurato l'input, definiamo l'output selezionando il tipo di container della risposta, File o Variabile. Qui di seguito l'esempio su variabile, dichiarata all'inizio del post:


    Eseguendo un ipotetico SSIS che lancia il web service suddetto e che in seguito visualizza il valore della risposta avremo questo comportamento:


    Dopo aver introdotto il task, vorrei soffermarmi su alcuni ostacoli che potremmo incontrare nell'usarlo. E vorrei anche giustificare il titolo del post .
    Oltre a non poter passare parametri di un certo tipo (da MSDN: "The Web Service task supports parameters of the following data types only: primitive types such as integers and strings; arrays and sequences of primitive types; and enumerations."), vi è da ricordare che tutte le risposte del web service task sono stringhe. Ognuna di esse è la serializzazione del valore/oggetto di ritorno. Quindi ogni stringa contiene l'XML della risposta serializzata. Questo perchè il task non si pone di creare una classe proxy come ad esempio Visual Studio fa quando aggiungiamo una Web Reference.
    Abbiamo la possibilità di salvare la stringa su un file xml, è vero, ma se volessimo utilizzare la risposta tipizzandola e castandola in un nostro oggetto all'interno di un package, dovremmo per forza ricorrere alla deserializzazione. E quindi scrivere codice.
    Di conseguenza, molto spesso non utilizzo il web service task ma direttamente lo script task, in un modo un po' particolare.

    Tramite il tool WSDL.exe usato anche da Visual studio per scrivere la classe proxy dal wsdl, genero la classe proxy.


    Aggiungo uno script task e includo al suo interno la classe generata con wsdl.exe (con SSIS 2005 solo VB). Inoltre aggiungo il riferimento a System.Web.Services e System.Xml.



    Nello ScriptMain possiamo istanziare il manager del servizio (la nostra classe proxy) per eseguire i metodi da codice. Ricordiamo il metodo d'esempio:

    <WebMethod()> _
        Public Function NumeroRighe(ByVal ds As DataSet) As Integer
           
    Return ds.Tables(0).Rows.Count
        End Function

    Ecco come nello Script eseguo la chiamata:

    Imports System
    Imports System.Data
    Imports System.Math
    Imports Microsoft.SqlServer.Dts.Runtime

    Public Class ScriptMain

        Public Sub Main()

            ' istanzio il web service
            Dim objWS As New Service1()


           
    ' creo una tabella con una riga
            Dim objdt As New DataTable()

            objdt.Columns.Add("col1", GetType(System.String))
            Dim objdr As DataRow = objdt.NewRow()

            objdr("col1") = "PROVA"
            objdt.Rows.Add(objdr)

            ' popolo il dataset
            Dim objDs As New DataSet
            objDs.Tables.Add(objdt)

            ' chiamo il metodo che mi torna il numero di righe della tabella del dataset
            Dim i As Integer = objWS.NumeroRighe(objDs)

            ' ...

            Dts.TaskResult = Dts.Results.Success

        End Sub

    End Class

    Con questo metodo possiamo muoverci con maggiore libertà sui tipi del framework ed anche su quelli definiti da noi. Esiste comunque un rovescio della medaglia, ed è quello relativo alla manutenzione del codice. Cambiando la struttura del web service, è necessario replicare la nuova classe proxy anche negli script che la utilizzano.
    ATTENZIONE
    Vi è una soluzione più corretta, ovvero creare una dll strongly signed e metterla in GAC, in modo tale da poter aggiungere il riferimento così come per le altre classi .NET. Quest'ultima è sicuramente la soluzione più pulita, se non vogliamo utilizzare il task di SSIS. Ad ogni modo, il riferimento deve essere aggiunto a livello di Script poichè non è possiible referenziare una DLL a livello globale.
    Comunque lascio la firma e la creazione dell'assembly in GAC a Voi.. ecco un link utile per capire meglio come firmare la dll
    http://msdn2.microsoft.com/en-us/library/ms247123.aspx

    Stay tuned!
  • [SSIS] - I DataFlow - le trasformazioni

    Ritorniamo alla nostra allungatissima lezione introduttiva su SSIS , anche se alcuni di voi ormai avranno raggiunto uno skill talmente elevato da non dover nemmeno considerare questa pagina. Ma è il mio progetto iniziale e quindi lo porto a termine, cercando di coprire un po' tutti gli argomenti su Integration Service, perlomento quelli basilari .
    L'ultima volta ci eravamo fermati alle destinazioni dei DataFlow. In questo post cominceremo ad affrontare l'argomento più vario della sezione relativa al flusso dati, le Trasformazioni.
    Cosa sono? Generalizzando al massimo, mentre le sorgenti sono veri e propri "generatori" di dati e le destinazioni i "contenitori", tutto quello che sta nel mezzo è una trasformazione. Un dataflow può essere formato anche da una o più sorgenti che mappano una o più destinazioni solamente, ma in logiche complesse, i task di trasformazione sono necessari per portare le informazioni dallo stato A in cui si trovano alla sorgente, verso lo stato B necessario alla destinazione. Nella sigla ETL, di cui SSIS rappresenta una delle implementazioni, le trasformazioni sono proprio la T (Extract, Transform and Load).
    Per fare semplici esempi, pensate ad alcuni dati che arrivano denormalizzati su txt (in post precedenti, nella sezione SSIS Tips abbiamo avuto modo di vedere già alcuni Dataflow con trasformazioni) e che devono essere portati su strutture più "normali" su sql server, access, excel. O viceversa, dati provenienti da vari RDBMS che necessitano di essere scaricati su file flat, csv, xml, altre piattaforme.. e via discorrendo.

    Le trasformazioni possono essere suddivise in quattro macrocategorie:

    - Business Intelligence Transformations
      Permettono di implementare logiche di business sui dati (ad esempio calcoli di Data Minig).

    - Row Transformations
      Consentono la creazione e l'aggiornamento di colonne in base alla riga passata in input al task (ad esempio Colonne derivate, Script component, Conversioni di date, Comandi OLEDB per riga)

    - Rowset Transformations
      Consentono la creazione di set di dati aggregati, ordinati, trasposti (ad esempio il PIVOT/UNPIVOT, i task di raggruppamento, il task sort)

    - Split and Join Transformations

      Consentono la distribuzione di un input in più output, l'unione di più input in un output oppure eseguono operazioni di lookup (multicast, union e lookup task)

    Tutte le trasformazioni che non rientrano in questi quattro gruppi (ad esempio i RowCount, le Slowly Changing Dimension, ecc) sono stati categorizzati da Microsoft come Other Transformations.

    Ho deciso di saltare il primo gruppo, quello legato alla BI, soprattutto per frequenza di utilizzo nella maggior parte delle applicazioni in cui i SSIS vengono applicati. Vi lascio comunque la reference su MSDN qualora doveste averne bisogno. L'elenco dei task relativo a questa sezione è:
    - Fuzzy Grouping
    - Fuzzy Lookup
    - Term Extraction
    - Term Lookup
    - Data Mining Query

    Passiamo quindi alle Row transformations. L'elenco è il eguente e anche in questo caso lascio il link ad MSDN:
    - Character Map Transformation
    - Copy Column Transformation
    - Data Conversion Transformation
    - Derived Column Transformation
    - Script Component
    - OLE DB Command Transformation

    Le tre maggiormente utilizzate nella mia esperienza da SSIS developer sono state la Derived Column, la Data Conversion e lo Script component.
    Con la prima è infatti molto semplice modificare i metadati provenienti da una sorgente. Grazie a Derived Column infatti è possibile aggiungere colonne, indicando un nuovo nome ed una espressione che riassume il valore che conterrà quel particolare campo.
    Facciamo un esempio. Supponiamo di avere una sorgente dati che ci passa un campo particolare, che deve essere sia elaborato, sia tenuto com'era in origine. Ad esempio, l'età di una persona e la sua età fra 20 anni (esempio semplificato al massimo, non è necessario un SSIS per questo "problema" ).
    In questo contesto possiamo creare una colonna Eta20, derivandola da Eta ed aggiungendo un'espressione data dalla colonna Eta + 20:


    Si seleziona la colonna e si definisce la conversione. In questo caso, parto dalla colonna Eta20 e arrivo ad una nuova colonna strEta20, di tipo stringa con CodePage 1252 (ANSI Latin) e lunghezza 3 caratteri. Come per la derived column possiamo controllare l'input del task e l'output. Nel nostro caso in input ci sono le tre colonne di output del derived column task ed in output c'è la colonna strEta20 in più.

    Seppure la trasformazione Copy Column sia comunque utilizzata, preferisco saltarla; noterete come si avvicina alla Data Conversion (molto più semplice di quest'ultima). Anche la OLEDB Command, che si discosta però da tutte le trasformazioni che stiamo affrontando, non verrà trattata in questo post. E' sufficiente sapere che esegue il comando riga per riga. Quindi prende la riga in input ed esegue un comando SQL. Attenzione a non abusarne, proprio per la sua logica ciclica. Mi è capitato di utilizzarla per gestire alcuni lookup errati, in modo da salvarli all'interno di tabelle di log. Può risultare utile anche per "appoggiare" i dati non validi al momento su di una tabella che viene controllata periodicamente al fine di cercare di ripristinare eventuali record.
    Comunque sia vi ho consiglio di leggere la reference su MSDN.

    Terminiamo in vece con la trasformazione più particolare, proprio perchè potrebbe anche non essere una vera e propria trasformazione. Lo Script Component. Ogni qual volta esso viene trascinato sullo stage la prima richiesta è "come deve comportarsi?" e le possibili risposte sono "sorgente", "destinazione", "trasformazione". Il caso che ci interessa è proprio quest'ultimo.
    Partiamo dal presupposto che anche questo task si comporta come le transformation di cui abbiamo parlato; ha un input (che porta con se metadati) e un output.
    Quello che trasforma è la logica dello script, quindi quello che andiamo a scrivere noi. Si utilizza quando altri task non soddisfano le nostre richieste, ma come ho già detto qui e qui, può essere comodo anche per bypassare degli scomodi comportamenti di altri componenti.

    Supponiamo, per comodità, di dover ripetere le funzioni definite dai due precedenti task. Partiamo da una tabella che ha il campo Eta e vogliamo arrivare a strEta20. Con lo script, come prima cosa si sceglie la natura del component:



    Successivamente si selezionano gli input, ovvero i metadati derivandi dal task precedente:



    Da notare la colonna UsageType che determina se le colonne possono essere anche scritte, oltre che lette. La fase seguente consiste nel preparare l'output:



    Si rinomina l'output (operazione facoltativa) poi si aggiungono tante colonne quante si vogliono aggiungere in output del task ed infine si definiscono i rispettivi parametri (tipo di dato, lunghezza, codepage, precisione, scala, ecc..). Infine si progetta lo script:





    Il metodo già proposto da SSIS è Input0_ProcessInputRow tramite il quale è possibile usare l'oggetto Row, passato come parametro, come se fosse la riga corrente. Ed è proprio in questo metodo che ho scritto la somma e la conversione. Vi sono poi altri metodi su cui è possibile agire:



    Per questi leggete questo link relativo ai metodi dello script component.

    Mi fermo qui nell'introdurre le trasformazioni. Dobbiamo affrontare ancora due gruppi, e nei prossimi post vedremo come comportarci con le possibilità che Integration Services ci fornisce.
    Vedendo una prima carrellata, è normale pensare che lo script potrebbe risolvere molti dei vostri problemi, comunque sia, vi consiglio di capire e conoscere bene gli strumenti e le altre funzionalità che il resto dei task possono darvi. Questo per comprendere se il codice scritto da voi (noi, mi ci metto anche io )  può darvi veramente dei vantaggi.
    Ricordatevi che l'architettura di SSIS è ottimizzata ed è in grado di parallelizzare le operazioni (nonchè gestire la memoria in maniera molto intelligente). Quindi fate prima attenzione a tutto quello che potete provare prima di arrivare a scrivere "vagonate" di codice.

    Stay tuned!