05/10/2009

Raportowanie

Home
Ostatnio poznałem nieznany mi wcześniej, a prosty sposób tworzenia raportów i zapisywania ich do formatu PDF czy Excel. Działa on zarówno w kontekście aplikacji ASP.NET jak i w aplikacjach Windows Forms i innych. Mam tutaj na myśli klasy z przestrzeni nazw Microsoft.Reporting.WebForm (w przypadku aplikacji stacjonarnych chodzi o przestrzeń Microsoft.Reporting.WinForms).

W przestrzeni Microsoft.Reporting.WebForm znajdziemy wiele rzeczy, najważniejsze to po pierwsze kontrolka ReportViewer do prezentowania raportów. Po drugie klasy LocalReport oraz ServerReport służące odpowiednio wykonywaniu raportów lokalnie oraz zdalnie na serwerze. Źródłem danych dla raportów może być oczywiście relacyjna baza danych ale również obiekty biznesowe. Definicja raportu to dokument XML stworzony przy pomocy języka RDL (ang. Report Definition Language).

Zanim przeję dalej podam założenia/wymagania jakimi się kierowałem:
  • Raporty chcę zapisywać do formatu PDF i Excel.
  • Proces generowania raportu ma odbywać się lokalnie bez połączenia z bazą danych.
  • Dane mam zapisane w obiekcie klasy DataTable.
Po pierwsze musiałem znaleźć sposób dynamicznego generowania definicji raportów. Język RDL nie wygląda na trudny ale nie zmienia to faktu, że go nie znam. Po krótkich poszukiwaniach napotkałem na ten artykuł Lesson 4: Creating Code to Generate the Report Definition File. Zaprezentowany tam kod tworzy plik z definicją raportu jako dane wejściowe przyjmując: listę pól/kolumn, zapytanie do bazy danych oraz connection string. Jak widać kod jest przydatny ale nie do końca ponieważ nie odpowiada postawionym wymaganiom. Jak sie jednak okazało konieczne modyfikacje były bardzo proste. Pomijając zmianę argumentów wejściowych metody, delikatną modyfikację logiki wystarczyło, że jako zapytanie do bazy danych oraz connection string przekazałem pusty ciąg znaków String.Empty. Cała metoda jest dość długa ale w gruncie rzeczy nie zawiera niczego skomplikowanego. Dopowiem tylko, że liczba kolumn generowanego raportu zależy od liczby kolumn w przekezanym do metody obiekcie klasy DataTable. Pełny kod został pokazany poniżej.

Pokaż/Ukryj kod

Proces generowania raportu wynikowego w żądanym formacie również jest prosty. Poniższy fragment kody generuje raport w formacie PDF. Żeby wytworzyć arkusz programu Excel to metody Render należy przekazać ciąg znaków "Excel". Zmienna reportPath powinna zawierać ścieżkę do pliku z definicją raportu stworzonego przez pokazaną wcześniej metodę. Zmienna dataTable to z kolei tabela z danymi do zapisania w żądanym formacie. Powinna to być ta sama tabela, która została przekazana do metody GenerateRdl.
...
FileStream fs = File.OpenWrite( reportPath);
GenerateRdl(fs, dataTable);

LocalReport lr = new LocalReport();
lr.ReportPath = reportPath;
lr.DataSources.Add(new ReportDataSource("DummyDataSet", dataTable));

string deviceInfo = "<DeviceInfo><SimplePageHeaders>True</SimplePageHeaders></DeviceInfo>";
string mimeType = null;
string encoding = null;
string ext = null;
string[] streamids = null;
Warning[] warnings = null;
byte[] bytes = lr.Render("PDF", deviceInfo, out mimeType, out encoding, out ext, out streamids, out warnings);
...

5 comments:

Anonymous said...

Artykul bardzo fajny, mam jedno tylko pytanie
A jak podłączyć przestrzeń nazw Microsoft.Reporting.WebForm ? na serwerze iis nie mam zainstalowanego Reporting services

Michał Komorowski said...

Wystarczy, że odpowiednie dll'ki będą w GAC'u na maszynie, na której hostujesz aplikację. Jeśli jest to nie możliwe to wystarczy, że wrzucisz te dll'ki do katalogu bin aplikacji.

Anonymous said...

No cos nie dziala. Przy stworzonej tabeli
DataTable dt = new DataTable();
dt.Columns.Add(new DataColumn("Lp"));
dt.Columns.Add(new DataColumn("Name"));

DataRow dr = dt.NewRow();
dr["Lp"] = "1";
dr["Name"] = "Ziomal";
dt.Rows.Add(dr);
I podpięcie pod dany kod wywala mi błąd:
"The input XML is not well-formed. One or more elements is missing a closing tag or has mismatched tags:..."
w metodzie byte[] bytes = lr.Render("PDF", deviceInfo, out mimeType, out encoding, out ext, out streamids, out warnings);

Michał Komorowski said...
This comment has been removed by the author.
Michał Komorowski said...

W pokazanym kodzie umieściłem nawiasy < oraz > i silnik bloga wyciął je. W każdym razie zamiast:


string deviceInfo = "True"'


powinno być:


string deviceInfo = "<DeviceInfo><SimplePageHeaders>True</SimplePageHeaders></DeviceInfo>";

Post a Comment