Quick Start Guide
Majorsilence Reporting is an open-source .NET reporting framework for generating PDFs, Excel files, HTML, CSV, and more from SQL databases, JSON files, or C# objects. It runs cross-platform on .NET 8 and .NET 10, and is built around the open RDL (Report Definition Language) standard.
Installation
Add the packages you need via NuGet:
# Core rendering engine (required)
dotnet add package Majorsilence.Reporting.RdlEngine.SkiaSharp
# Barcode / QR Code support via ZXing.Net (optional)
dotnet add package Majorsilence.Reporting.RdlCri.SkiaSharp
# Programmatic report & document creation (optional)
dotnet add package Majorsilence.Reporting.RdlCreator.SkiaSharp
Key Concepts
RdlEngineConfigInit() — call this once at application startup before parsing any reports. It loads data provider registrations and CRI type mappings from RdlEngineConfig.xml.
Parse-time DB connection — when RDLParser.Parse() is called it opens the database to validate the query schema. This means the connection string inside the RDL must resolve to a real database before Parse() returns. The standard pattern is to inject the absolute path into the RDL XML string before calling Parse().
RunGetData / RunRender — call RunGetData once to execute queries, then call RunRender once per output format. Each render pass rebuilds pages from the already-fetched data without re-querying the database.
Render a SQLite report to PDF
using Majorsilence.Reporting.Rdl;
// One-time setup
RdlEngineConfig.RdlEngineConfigInit();
var baseDir = AppContext.BaseDirectory;
var dbPath = Path.Combine(baseDir, "northwind.db");
var outPath = Path.Combine(baseDir, "products.pdf");
// Inject absolute DB path before Parse() so schema validation succeeds
var rdlXml = (await File.ReadAllTextAsync(Path.Combine(baseDir, "Products.rdl")))
.Replace("northwind.db", dbPath);
var parser = new RDLParser(rdlXml) { Folder = baseDir };
using var report = await parser.Parse();
if (report.ErrorMaxSeverity > 4)
{
foreach (var err in report.ErrorItems)
Console.Error.WriteLine(err);
return;
}
await report.RunGetData(null);
var ofs = new OneFileStreamGen(outPath, true);
await report.RunRender(ofs, OutputPresentationType.PDF);
Export to multiple formats in one pass
await report.RunGetData(null); // fetch data once
foreach (var (filename, format) in new[]
{
("report.pdf", OutputPresentationType.PDF),
("report.xlsx", OutputPresentationType.Excel2007),
("report.csv", OutputPresentationType.CSV),
("report.html", OutputPresentationType.HTML),
})
{
var ofs = new OneFileStreamGen(Path.Combine(baseDir, filename), true);
await report.RunRender(ofs, format);
Console.WriteLine($"Written: {filename}");
}
Read data from a JSON file
Declare the data source in your RDL with the Json provider. Nested objects are accessed using underscore notation (Contact_Email for contact.email).
<DataSource Name="DS1">
<ConnectionProperties>
<DataProvider>Json</DataProvider>
<ConnectString>file=employees.json</ConnectString>
</ConnectionProperties>
</DataSource>
<DataSet Name="Data">
<Query>
<DataSourceName>DS1</DataSourceName>
<CommandText>columns=EmployeeID,FirstName,LastName,Department,Contact_Email</CommandText>
</Query>
<!-- Field elements for each column... -->
</DataSet>
In C#, inject the absolute path before parsing:
var rdlXml = (await File.ReadAllTextAsync("Employees.rdl"))
.Replace("employees.json", Path.Combine(baseDir, "employees.json"));
Pass report parameters
Use RunGetData(IDictionary<string, string>) to supply parameter values. To filter by a SQL column, inject the value directly into the RDL SQL string before Parse() — passing a query parameter value post-parse will fail because the database tries to bind it during schema validation.
var country = "Germany";
var rdlXml = (await File.ReadAllTextAsync("CustomersByCountry.rdl"))
.Replace("northwind.db", dbPath)
.Replace("'Germany'", $"'{country.Replace("'", "''")}'"); // inject filter
var parser = new RDLParser(rdlXml) { Folder = baseDir };
using var report = await parser.Parse();
// Parameters are still passed for use in report expressions like page titles
var parameters = new Dictionary<string, string> { ["Country"] = country };
await report.RunGetData(parameters);
Add a QR Code or barcode (CRI)
Reference Majorsilence.Reporting.RdlCri and place a <CustomReportItem> in your RDL:
<CustomReportItem Name="ProductQR">
<Type>QrCode</Type>
<Top>2pt</Top><Left>2pt</Left>
<Width>72pt</Width><Height>72pt</Height>
<CustomProperties>
<CustomProperty>
<Name>Code</Name>
<!-- Expression bound to data field -->
<Value>=Fields!ProductName.Value</Value>
</CustomProperty>
</CustomProperties>
<Source>Embedded</Source>
</CustomReportItem>
Supported types (registered in RdlEngineConfig.xml):
| Type name | Format | Notes |
|---|---|---|
QrCode |
QR Code | Square; use equal Width/Height |
BarCode128 |
Code 128 | Landscape, ~2:1 ratio |
BarCode39 |
Code 39 | Landscape, ~2:1 ratio |
DataMatrix |
Data Matrix | Landscape, ~2:1 ratio |
AztecCode |
Aztec | Square |
Pdf417 |
PDF 417 | Landscape |
ITF-14 |
ITF-14 | Property name is ITF14; value must be 13–14 digits |
To select the barcode type at runtime, use a report parameter expression:
<Type>={?BarcodeType}</Type>
Inject data from C# objects
Use DataSet.SetData() to bypass the SQL query and feed data directly from a collection. The RDL still needs a real database at parse time for schema validation; use a LIMIT 0 stub query whose column aliases match your record’s property names.
record SaleRecord(string Product, string Region, decimal Amount, int Quantity);
var data = new List<SaleRecord>
{
new("Widget A", "North", 1250.00m, 50),
new("Widget B", "South", 840.50m, 33),
};
// Property names must match the RDL Field names exactly
await report.DataSets["Data"].SetData(data);
await report.RunGetData(null);
await report.RunRender(ofs, OutputPresentationType.PDF);
Create a report programmatically
Use Majorsilence.Reporting.RdlCreator to generate an RDL from a SQL query without a pre-made .rdl file:
using Majorsilence.Reporting.RdlCreator;
RdlEngineConfig.RdlEngineConfigInit();
var create = new Create();
using var report = await create.GenerateRdl(
dataProvider: "Microsoft.Data.Sqlite",
connectionString: $"Data Source={dbPath}",
sql: "SELECT ProductID, ProductName, UnitPrice FROM Products",
pageHeaderText: "Product Catalog");
var ofs = new Majorsilence.Reporting.Rdl.OneFileStreamGen("catalog.pdf", true);
await report.RunGetData(null);
await report.RunRender(ofs, Majorsilence.Reporting.Rdl.OutputPresentationType.PDF);
Reference
- Wiki home — full documentation
- All wiki pages
- Migration to v5
- JSON Data Provider
- Dynamic connection strings
- Contributing