Condividi tramite


VB.NET: Creare e Utilizzare Report indipendenti da DataSet (it-IT)


Obiettivo

In questo articolo vedremo un metodo per utilizzare un Local Report esistente in congiunzione ad una fonte dati arbitraria, o - in altre parole - come sia possibile disegnare un Local Report mediante Visual Studio e salvare il file RDLC per utilizzo futuro, nell'ambito di un controllo ReportViewer a cui venga passata una fonte dati dinamica, per esempio originata da una query. Vedremo come ottenere tale risultato mediante Visual Basic .NET.

Prerequisiti

Introduzione

Mi sono recentemente trovato nella necessità di dover redigere un programma di reporting che fosse il più possibile disgiunto da eventuali DataSet creati e gestiti lato IDE, o - più genericamente - non generati da una connessione o schema predeterminati, e che permettesse il rendering grafico di query arbitrarie. Il mio obiettivo principale è stato quello di evitare di avere diversi schemi dati in diverse applicazioni di reporting, cercando invece di creare un programma che potesse eseguire un file delle opzioni in cui specificare la connessione dati cui agganciarsi, la query da eseguire con i suoi parametri (da richiedere all'utente) ed un file RDLC da utilizzare per il rendering grafico dei dati, attraverso un controllo ReportViewer. In poche parole, ho cercato di creare un reporter indipendente, che potesse essere utilizzato in modi diversi semplicemente sostituendo il file delle opzioni di lancio.

In questo articolo mostrerò un metodo per realizzazione una pressoché totale separazione tra il file RDLC e la business logic, per gettare le basi verso applicazioni più complesse, in cui un approccio del genere possa risultare utile, come è stato nel mio caso.

Seguendo il wizard

Prima di vedere come sia possibile bypassare il binding esplicito di una fonte dati, vediamo come Visual Studio gestisce normalmente la richiesta di creare una nuova applicazione di reporting. 
Apriamp Visual Studio, selezioniamo "New Project", quindi "Visual Basic" » "Reporting". Vediamo nell'immagine seguente la presenza di un template chiamato "Reports Application". 

Confermando la volontà di procedere, l'IDE eseguirà i seguenti passaggi: a) creazione di un Form su cui risederà un controllo ReportViewer, b) creazione di un file denominato Report1.rdlc, ovvero la renderizzazione grafica del nostro report e c) lancio del wizard utile alla connessione verso una fonte dati, selezionando gli oggetti che diverranno parte della business logic dell'applicazione. L'immagine seguente mostra la prima videata di tale wizard.

Qui a seguire vediamo invece la parte del wizard in cui ci viene richiesto di specificare le entità che dovranno essere utilizzate:

Dopo aver scelto la tipologia di connessione, e gli oggetti da utilizzare, il wizard proseguirà nella creazione di un DataSet, corredato da un file XSD rappresentante lo schema dati. Ci verranno quindi mostrati i campi presenti nelle entità scelte, per esempio una tabella.

A questo punto, il file RDLC potrà essere modificato, aggiungendo elementi grafici e indicando quali campi debbano essere riportati ed in quali posizioni, semplicemente trascinando gli stessi dal DataSet cui avremo accesso dalla barra laterale: il wizard avrà infatti creato due ulteriori elementi, ovvero un TableManager ed un TableAdapter, tramite i quali accedere ai dati fisici presenti nella connessione indicata.

Ciò che risulta evidente è che, stando a quanto l'IDE ha fatto per noi, per ciascun report sarà necessario preparare un DataSet e successivi collegamenti dati per ogni singolo ReportViewer. Questo è un metodo semplice e tutto sommato rapido nel caso ci trovassimo a dover gestire un paio di report, magari in una singola applicazione. Nel caso però si necessiti di maggiore flessibilità, multi-reportistica, utilizzo trasversale dei report a diverse applicazioni, quanto sopra può trasformarsi in una limitazione. ed è proprio a questo punto che entra in scena il nostro piccolo "hack".

Analisi di un file RDLC

Un file RDLC, il quale definisce un Local Report, è un semplice file XML, e - come tale - può essere aperto anche con un comune editor di testo. Se apriamo il nostro file Report1.rdlc, creato dall'IDE, vedremo che tutto ciò che può essere rappresentato graficamente ha la sua trasposizione XML. E ciò è vero anche nel caso della sezione dati. Nelle prossime due immagini, osserviamo alcuni tag interessanti in merito alla presentazione dei dati. Nella prima, notiamo una sezione "DataSources", la quale contiene il nostro DataSet con le sue proprietà; nella seconda immagine, notiamo la presenza di tag dedicati allo schema del DataSet, la connessione (che verrà poi salvata nel file di configurazione dell'applicazione), e alcuni tag chiamati "Fields", utilizzati per specificare il nome, tipo, nome dei membri dato, ecc. di ciascun campo del DataSet stesso.

Cosa accadrebbe se dovessimo rimuovere da un file RDLC ogni referenza a schemi, DataSets, lasciando solo le referenze ai campi? Potrebbe ancora essere utilizzato?
Vediamo come un file RDLC possa essere gestito dopo tali modifiche.

Modifica di un Report

Rimuovendo dal file RDL tutte le referenze ai dati, ciò che rimane nel mio caso è quanto segue:

<?xml version="1.0" encoding="utf-8"?>
<Report xmlns:rd="http://schemas.microsoft.com/SQLServer/reporting/reportdesigner" xmlns:cl="http://schemas.microsoft.com/sqlserver/reporting/2010/01/componentdefinition" xmlns="http://schemas.microsoft.com/sqlserver/reporting/2010/01/reportdefinition">
  <AutoRefresh>0</AutoRefresh>
  <DataSources>
    <DataSource Name="DataSet">
      <ConnectionProperties>
        <DataProvider>System.Data.DataSet</DataProvider>
        <ConnectString>/* Local Connection */</ConnectString>
      </ConnectionProperties>
      <rd:DataSourceID>fda0d98e-6996-4a1d-bfe9-19b29fa568f9</rd:DataSourceID>
    </DataSource>
  </DataSources>
  <DataSets>
    <DataSet Name="DataSet1">
      <Query>
        <DataSourceName>DataSet</DataSourceName>
        <CommandText>/* Local Query */</CommandText>
      </Query>
      <Fields>
        <Field Name="Variable01">
          <DataField>Variable01</DataField>
          <rd:TypeName>System.String</rd:TypeName>
        </Field>
        <Field Name="Variable02">
          <DataField>Variable02</DataField>
          <rd:TypeName>System.String</rd:TypeName>
        </Field>
        <Field Name="Variable03">
          <DataField>Variable03</DataField>
          <rd:TypeName>System.String</rd:TypeName>
        </Field>
      </Fields>
    </DataSet>
  </DataSets>
  <ReportSections>
    <ReportSection>
      <Body>
        <Height>2in</Height>
        <Style />
      </Body>
      <Width>8.47513in</Width>
      <Page>
        <PageHeight>29.7cm</PageHeight>
        <PageWidth>21cm</PageWidth>
        <LeftMargin>1cm</LeftMargin>
        <RightMargin>1cm</RightMargin>
        <TopMargin>1cm</TopMargin>
        <BottomMargin>1cm</BottomMargin>
        <ColumnSpacing>0.13cm</ColumnSpacing>
        <Style />
      </Page>
    </ReportSection>
  </ReportSections>
  <rd:ReportUnitType>Cm</rd:ReportUnitType>
  <rd:ReportID>809a73c6-2cff-4373-b2bd-e4703abea631</rd:ReportID>
</Report>

Nessuna connessione, nessuno schema, ed un DataSet "fasullo" di nome DataSet1, che contiene tre campi, rispettivamente Variable01, Variable02, Variable03. Per mantenere l'esempio semplice, supponiamo che in tutti e tre i casi si tratti di dati di tipo System.String type (ma ovviamente ciò è modificabile in base alle proprie esigenze). La cosa interessante è che il file continuerà ad essere un file RDLC perfettamente funzionante, ovvero Visual Studio continuerà ad aprirlo e permetterci variazioni al suo interno. 

Ma cosa accade in apertura del file?

Sì, viene mostrato il nostro DataSet "fasullo" con i suoi campi. Essi potranno essere trascinati sul report, sempre editabile e modificabile. Quindi possiamo adesso procedere nel modificare graficamente tutto ciò di cui abbiamo bisogno, aggiungendo elementi, tabelle, dati, e così via. Ma abbiamo anche necessità di qualcosa che possa incorporare il nostro file RDLC (un ReportViewer) e che possa indirizzare verso di esso dei dati reali, per popolare i campi del DataSet "fasullo" durante il run-time.

Query su DataSource e binding

Per eseguire l'ultima parte del nostro lavoro, imiteremo ciò che il wizard ha precedentemente fatto in maniera trasparente per noi. Ha creato un DataSet, probabilmente passando per una DataTable, e mediante un DataAdapter ha ottenuto accesso ai dati fisici sottostanti, collegandoli ai campi corrispondenti del report. Utilizzando una semplice routine in Visual Basic .NET, noi faremo lo stesso:

Dim dt As New  DataTable
Using conn As  New SqlConnection("YOUR_CONNECTION_STRING_GOES_HERE")
  conn.Open()
  
  Dim _sqlCommand As New  SqlCommand("SELECT ProductCode AS Variable01, ProductDes AS Variable02, Barcode AS Variable03 FROM
  
SampleTable", conn)
  _sqlCommand.CommandTimeout = 4096
  Dim ta As New  SqlDataAdapter(_sqlCommand)
  ta.Fill(dt)
End Using
  
With Me.ReportViewer1.LocalReport
   .DataSources.Clear()
   .ReportPath = "THE_PATH_TO_A_PHYSICAL_RDLC_FILE"
   .DataSources.Add(New Microsoft.Reporting.WinForms.ReportDataSource("DataSet1", dt))
End With
  
Me.ReportViewer1.RefreshReport()

Si ponga attenzione alla query SQL. In essa, abbiamo utilizzato per ciascun campo l'alias con il quale il report si aspetta un certo campo venga chiamato, in modo che - quando il ReportViewer eseguirà il rendering dei dati - potrà effettuare correttamente il binding tra i nostri campi di query e i campi corrispondenti del report.

Conclusione

Avendo visto come separare un report dalla logica di business dell'applicazione, diventa semplice sviluppare programmi che si interfacciano con report differenti, separatamente costruiti e preparati. Questo ci permette di avere soluzioni più snelle, evitando la presenza di schemi, DataSets, logica dedicata, e quindi mantenendo un ordine maggiore e possibilità superiori di futura manutenzione.

Nello screenshot seguente, presento uno stamp del mio lavoro accennato in apertura, con alcuni dati oscurati. Esso si appoggia ad un file di parametri in cui specificare i filtri da richiedere all'utente, la query da eseguire, il report da visualizzare, e così via.

Altre lingue

Il presente articolo è disponibile nelle seguenti localizzazioni: