Implementacja: PrintReportWithDbSource
Opis scenariusza biznesowego
Klasa demonstracyjna PrintReportWithDbSource realizuje kluczowe mechanizmy integracyjne w architekturze bezpośredniego zasilania bazodanowego (Direct Database Seed). W tym modelu silnik sPrint API przejmuje odpowiedzialność za fizyczne otwarcie połączenia z relacyjną bazą danych MS SQL Server (w przykładzie zasilaną bazą testową NORTHWND), wykonanie sparametryzowanych zapytań strukturalnych oraz automatyczne zmapowanie zestawów rekordów na warstwę wizualną szablonu.
Klasa ta jest fundamentalnym przewodnikiem programistycznym prezentującym deweloperom trzy stopnie zaawansowania konfiguracji właściwości DataSources oraz tablic powiązań ReportIds w środowisku raportów zagnieżdżonych.
Analiza metod i strategii zasilania bazodanowego
1. Podstawowe zapytanie sparametryzowane (Print_CounterSprintDbMain_PathMethodology)
Metoda demonstruje zasilenie bazowe dla pojedynczego, liniowego szablonu CounterSprintDbMain.sp. W konfiguracji modelu DbSourceViewModel definiowane są standardowe parametry połączenia (serwer, port, nazwa bazy) oraz treść zapytania SQL zawierająca zmienną @Category_name. Silnik sPrint API automatycznie pobiera obiekt z tablicy Parameters, podstawia wartość tekstową w miejsce zmiennej SQL i przesyła zapytanie do bazy, zwracając przefiltrowany dokument.
2. Podraporty współdzielące źródło danych (Print_CounterSprintDbMainWithSubreport_OneDataSourceForeachReport)
Ten scenariusz demonstruje uproszczone zarządzanie strukturą dokumentu zawierającego zagnieżdżone podraporty (subreporty). W konfiguracji źródła danych tablica ReportIds zostaje celowo pozostawiona jako pusta: ReportIds = []. Dla silnika sPrint API jest to jednoznaczny sygnał architektoniczny (Implicit Default Binding) – wskazana konfiguracja serwera SQL oraz zapytanie mają zostać zastosowane bezwarunkowo jako nadrzędne i wspólne dla całego pakietu raportu, w tym dla wszystkich wykrytych podszablonów.
3. Zaawansowane wiązanie dedykowanych zródeł danych (Print_CounterSprintDbMainWithSubreport_2DifferentDataSourcesEach)
Najbardziej zaawansowany przypadek produkcyjny (Explicit Target Binding), niezbędny w skomplikowanych wydrukach systemów ERP. Metoda pokazuje sytuację, w której raport główny oraz zagnieżdżony podraport logistyczny wymagają wykonania zupełnie innych zapytań SQL (pobranie nagłówka kategorii kontra pobranie listy produktów powiązanych).
Deweloper za pomocą identyfikatorów GUID wyciągniętych wcześniej z endpointu POST /api/v0/Info jawnym wpisem w tablicy ReportIds decyduje, które zapytanie SQL ma zasilić raport główny, a które ma zostać wykonane wyłącznie na potrzeby wyizolowanego podszablonu.
Pełny kod źródłowy przykładu (C#)
Poniższa implementacja w języku C# dla platformy .NET 8 prezentuje produkcyjne zadeklarowanie modeli połączeń SQL, konfigurację poświadczeń (Credentials) oraz mechanizmy kontroli czasu wykonywania zapytań (CommandTimeout):
using Comarch.BI.CounterSprint.Integration.Client;
namespace Comarch.BI.CounterSprint;
public static class PrintReportWithDbSource
{
private static readonly string ReportPath = Path.GetFullPath(@"Packages\DbSource\CounterSprintDbMain.sp");
private static readonly string ReportWithSubreportPath = Path.GetFullPath(@"Packages\DbSource\CounterSprintDbMainWithSubreport.sp");
private static readonly string PrintResultPath1 = Path.GetFullPath(@"Packages\DbSource\CounterSprintDbMain1.pdf");
private static readonly string PrintResultPath2 = Path.GetFullPath(@"Packages\DbSource\CounterSprintDbMain2.pdf");
private static readonly string PrintResultPath3 = Path.GetFullPath(@"Packages\DbSource\CounterSprintDbMain3.pdf");
/// <summary>
/// Prints the CounterSprintDbMain report using path methodology.
/// </summary>
public static async Task Print_CounterSprintDbMain_PathMethodology(IntegrationApiClient client)
{
var operationId = Guid.NewGuid().ToString();
LogStep($"Generating information about CounterSprintDbMain.sp report, operation ID: {operationId}");
var infoRequest = new InfoViewModel
{
OperationId = operationId,
Report = new PathSourceViewModel { Path = ReportPath }
};
var infoResponse = await client.InfoAsync(infoRequest);
LogStep($"Printing report CounterSprintDbMain.sp, with db data source to pdf, operation ID: {operationId}");
var request = new PrintViewModel()
{
OperationId = Guid.NewGuid().ToString(),
Report = new PathSourceViewModel { Path = ReportPath },
PrintFormat = FileFormat.Pdf,
Output = new PathOutputViewModel { Path = PrintResultPath1 },
DataSources = [
new DbSourceViewModel()
{
Server = "tesla",
DatabaseName = "NORTHWND",
Port = 1433,
CommandTimeout = 1200,
ConnectionTimeout = 1200,
ConnectionType = DbConnectionType.MsSql,
Credentials = new CredentialsViewModel { Login = "", Password = "" },
Query = "Select * from \"Categories\" where \"CategoryName\" = @Category_name",
}
],
Parameters = [
new ParameterToReplaceViewModel()
{
Name = "Category_name",
Value = "Confections"
}
]
};
await client.PrintAsync(request);
LogStep($"Report printed successfully to {PrintResultPath1}");
}
/// <summary>
/// Prints the CounterSprintDbMainWithSubreport report using a single data source (Implicit Binding).
/// </summary>
public static async Task Print_CounterSprintDbMainWithSubreport_OneDataSourceForeachReport(IntegrationApiClient client)
{
var operationId = Guid.NewGuid().ToString();
LogStep($"Generating information about CounterSprintDbMainWithSubreport.sp report, operation ID: {operationId}");
var infoRequest = new InfoViewModel
{
OperationId = operationId,
Report = new PathSourceViewModel { Path = ReportWithSubreportPath }
};
var infoResponse = await client.InfoAsync(infoRequest);
LogStep($"Printing report CounterSprintDbMainWithSubreport.sp, with db data source to pdf, operation ID: {operationId}");
var request = new PrintViewModel()
{
OperationId = Guid.NewGuid().ToString(),
Report = new PathSourceViewModel { Path = ReportWithSubreportPath },
PrintFormat = FileFormat.Pdf,
Output = new PathOutputViewModel { Path = PrintResultPath2 },
DataSources = [
new DbSourceViewModel()
{
ReportIds = [], // Pusta tablica oznacza powiązanie domyślne dla całego pakietu
Server = "tesla",
DatabaseName = "NORTHWND",
Port = 1433,
CommandTimeout = 1200,
ConnectionTimeout = 1200,
ConnectionType = DbConnectionType.MsSql,
Credentials = new CredentialsViewModel { Login = "", Password = "" },
Query = "Select * from \"Categories\" where \"CategoryID\" = @Category_id",
}
],
Parameters = [
new ParameterToReplaceViewModel()
{
Name = "Category_id",
Value = "1"
}
]
};
await client.PrintAsync(request);
LogStep($"Report printed successfully to {PrintResultPath3}");
}
/// <summary>
/// Prints the CounterSprintDbMainWithSubreport report using two different targeted data sources.
/// </summary>
public static async Task Print_CounterSprintDbMainWithSubreport_2DifferentDataSourcesEach(IntegrationApiClient client)
{
var operationId = Guid.NewGuid().ToString();
LogStep($"Generating information about CounterSprintDbMainWithSubreport.sp report, operation ID: {operationId}");
var infoRequest = new InfoViewModel
{
OperationId = operationId,
Report = new PathSourceViewModel { Path = ReportWithSubreportPath }
};
var infoResponse = await client.InfoAsync(infoRequest);
//Identyfikatory GUID sekcji pobrane z metody diagnostycznej /Info
//Main report Guid = 0a480220-e59c-4e53-9743-16cbdcb7a2dc
//Subreport Guid = 67119686-a7ce-4321-9f07-f67f056bb3fb
LogStep($"Printing report CounterSprintDbMainWithSubreport.sp, with db data source to pdf, operation ID: {operationId}");
var request = new PrintViewModel()
{
OperationId = Guid.NewGuid().ToString(),
Report = new PathSourceViewModel { Path = ReportWithSubreportPath },
PrintFormat = FileFormat.Pdf,
Output = new PathOutputViewModel { Path = PrintResultPath2 },
DataSources = [
new DbSourceViewModel()
{
// Jawne mapowanie zapytania wyłącznie do raportu głównego
ReportIds = ["0a480220-e59c-4e53-9743-16cbdcb7a2dc"],
Server = "tesla",
DatabaseName = "NORTHWND",
Port = 1433,
CommandTimeout = 1200,
ConnectionTimeout = 1200,
ConnectionType = DbConnectionType.MsSql,
Credentials = new CredentialsViewModel { Login = "", Password = "" },
Query = "Select * from \"Categories\" where \"CategoryID\" = @Category_id",
}
,
new DbSourceViewModel()
{
// Jawne mapowanie dedykowanego zapytania SQL do wnętrza podszablonu
ReportIds = ["67119686-a7ce-4321-9f07-f67f056bb3fb"],
Server = "tesla",
DatabaseName = "NORTHWND",
Port = 1433,
CommandTimeout = 1200,
ConnectionTimeout = 1200,
ConnectionType = DbConnectionType.MsSql,
Credentials = new CredentialsViewModel { Login = "", Password = "" },
Query = "Select * from \"Products\" where \"CategoryID\" = @Category_Id_subreport",
}
],
Parameters = [
new ParameterToReplaceViewModel()
{
Name = "Category_id",
Value = "2"
}
]
};
await client.PrintAsync(request);
LogStep($"Report printed successfully to {PrintResultPath2}");
}
private static void LogStep(string message)
=> Console.WriteLine($"[Demo PrintReportWithDbSource] {message}");
}
Kluczowe aspekty techniczne i parametryzacja
- Zarządzanie czasem wykonania (CommandTimeout): W strukturach produkcyjnych właściwość CommandTimeout została zdefiniowana na wartość 1200 sekund. Zapobiega to przerwaniu generowania skomplikowanych, wielopoziomowych podszablonów analitycznych przez mechanizm strażnika sieciowego (Gateway Timeout) w przypadku dużego obciążenia bazy danych systemu ERP.
- Mapowanie typów bazodanowych (ConnectionType): Właściwość ConnectionTypeprzyjmuje wartość słownikową DbConnectionType.MsSql. Informuje to warstwę abstrakcji sPrint, że sterownik ma użyć dedykowanego klienta SqlClient i zoptymalizować parsowanie dialektu T-SQL dla serwerów Microsoft SQL Server.
- Bezpieczeństwo poświadczeń (Credentials): Obiekt CredentialsViewModel pozwala na separację autoryzacji bazodanowej. W środowiskach domenowych, pozostawienie pustych pól Login oraz Password skutkuje użyciem Windows Authentication (Zintegrowane bezpieczeństwo konta systemowego usługi sPrint), natomiast wpisanie wartości SA lub usera integracyjnego wymusza SQL Server Authentication.