Migrating from SQL Server Reporting Services / A report designer or markup toolchain
Migrating off SSRS, from RDL and tablix to markup
A reporting service whose layouts are RDL files built around a tablix, with the report server providing scheduling, subscriptions and delivery alongside the rendering.
What ports and what does not
The datasets, which are SQL and which carry across directly. The RDL does not: it is XML, so it is at least readable, but the tablix is a single control doing the work of a table, a matrix and a grouped list at once, and unpicking which of those a given report is using is the first task. The larger question is the report server itself, because SSRS was never only a renderer. Subscriptions, scheduled delivery, the report portal and access control all live there, and a rendering API replaces one part of that.
What the call becomes
The report execution web service call, or a ReportViewer render, becomes a POST to /v1/pdf with html. Report parameters become template inputs. Everything the report server did around the render, scheduling, emailing, snapshotting, has no counterpart and needs somewhere else to live, which is the part of this migration to scope honestly before starting.
Side by side
The old shape and the new one, with the parts that have no counterpart called out in comments rather than quietly omitted.
// Before: rendering through the report server
byte[] pdf = rs.Render("/Invoices/Invoice", "PDF", null, deviceInfo,
parameters, null, null, out _, out _, out _, out _, out _);
// After
var payload = JsonSerializer.Serialize(new {
html = RenderInvoice(dataset, totals),
options = new { format = "A4" }
});
var resp = await http.PostAsync("https://api.pdfpipe.xyz/v1/pdf",
new StringContent(payload, Encoding.UTF8, "application/json"));
/* Scope the report server's other jobs before committing:
subscriptions and scheduled delivery -> a scheduler in your stack
emailing a rendered report -> your own mail path
the report portal -> wherever users get it now
row-level access control -> your application's authz
snapshots and history -> document storage; this API
has /v1/documents and
/v1/retrieve for storing
rendered output
The tablix translates by which mode it is in:
table mode -> a table with thead and tbody
matrix mode -> a table built from a pivoted dataset, pivoted in
your query rather than by the control
list mode -> a repeated block per row */The CSS delta, in both directions
What the old engine accepted that a current one does not, and what it refused that a current one wants. The second direction is the one people forget, and it is where the value of the migration is.
- Nothing to migrate. The RDL's style properties are readable XML and transcribe into CSS with effort but no ambiguity.
- Tablix grouping becomes headings and subtotal rows with break rules, the same as any grouped report.
- A matrix tablix is the awkward one: the control pivoted the data itself, and the replacement is to pivot in the query, which changes the dataset rather than the markup.
- Page setup in the RDL becomes options.format and options.margin.
What changes without anything erroring
The dangerous list. Each of these produces a different document and no diagnostic, so none of them are caught by a test that only checks the render succeeded.
- Everything visual, because it is a rebuild.
- Expressions in the RDL, which stop evaluating and take derived fields, conditional visibility and aggregates with them.
- Anything the report server was doing on a schedule, which stops happening rather than failing, and which nobody notices until somebody asks where the monthly report went.
- Page count.
What to diff before cutting over
Compare the numbers over the same parameters, and separately make a list of every subscription configured on the server. That list is the part of the migration that is not about documents at all, and it is the part most likely to be forgotten.
The one thing that always breaks
The subscriptions. The report renders, the new pipeline works, and the scheduled deliveries that were configured in the report portal years ago silently stop. Nobody is watching the report server once the reports have moved, so the first sign is a person asking why they no longer receive something.
Frequently asked
Do I have to rewrite my templates to leave SQL Server Reporting Services?
Effectively yes, and it is better to plan for that than to discover it. The old tool did not have HTML templates to port, so the document has to be expressed as markup for the first time. The value in the old code is the data layer underneath the drawing, and that part survives untouched.
Can I run both for a while?
Yes, and it is the safest way to do it. Put both behind one internal function that takes your data and returns bytes, switch on an environment variable, and run the new path on real traffic while the old one still serves. You get a diff on real documents rather than on fixtures, and you keep a way back that does not involve a deploy.
What about the documents already generated?
Nothing here changes them. They are files that already exist. What is worth deciding before the cutover is whether a regenerated document has to match the original byte for byte or merely say the same thing, because for anything with a legal or audit character the answer is usually to keep the original file rather than to be able to reproduce it.
Other migrations
The nearest neighbours first, then others that started from the same kind of tool, because the model matters more than the language.
Migrating off Crystal Reports, and rebuilding the formula fields
A reporting product whose layouts live in binary report files edited in a designer, with sections, formula fields and its own formula language, none of which are portable.
Migrating off JasperReports, and what happens to the bands
A reporting engine whose layouts are XML files built in a visual designer, organised into bands that repeat by rule, where nothing about the layout is portable and the band model has no equivalent.
Migrating off headless LibreOffice conversion, and its templates
An office suite run headless to convert documents to PDF, where the template is a word processor file and the conversion is a whole application invoked per document.
Migrating off DOCX templating, and keeping the Word deliverable
A pipeline that fills placeholders in a Word template and converts the result to PDF, where the conversion step is what you are replacing and the Word file may still be a deliverable.
Migrating off Apache FOP, from XSL-FO to CSS
A formatter taking XSL-FO, an XML vocabulary for paged layout, usually produced from your data by an XSLT transform, where the transform is the template and both halves change.
Every migration, grouped by what you are coming from
The full list, sorted by the model the old tool used rather than by its name.
What this API actually does
One page per option and endpoint that exists, which is what the mappings above point at.
Render one of your existing documents through the playground before changing any code. That comparison is the whole of the risk assessment for this migration.