The history of RDLC reporting

The RDLC report template gets compiled into a small assembly and then in the runtime, that assembly is used to parse the data set and hereby generate a PDF or EMF pictures for printing.

Inside reports, you can embed Visual Basic .NET code to do some advanced handling, for example. Depending on the code’s security asks, the report is either rendered in the server’sAppDomain or in another secure AppDomain that is generated by Report Viewer.

Running in the Report Viewer-generated AppDomainrequires a lot of marshaling in between the two AppDomains, which results inmuch longer rendering times on reports, which means performance issues. For that reason, we only wanted to run the reports in the Report ViewerAppDomain when it was really needed.

IMPORTANT: If you ran the reports in the server’s AppDomain, there would be a slight memory leak, because each of the created assemblies would never get unloaded in the server AppDomain. However, if the same was done in the isolated AppDomain, we would be able to get rid of the assemblies, by unloading the secure AppDomain. At the time, we decided that the trade-off between performance gain over the small leak was acceptable.

Just before we launched Dynamics NAV 2013 R2, we changed the .NET runtime to version 4.0. In .NET Framework 4.0, Code Access Security (CAS) was deprecated because it was way too complicated. Anyway, if you allowed another assembly to load in your process, it was better to trust them up front or isolate them completely.However, Report Viewer continuedto use the CAS policy. With the deprecation, all renderings were deferred to the Report Viewer-generated AppDomain and therefore running slowly with performance issues.

Over time, we realized that we could start the process with a CLR legacy switch:

This would enable us to achieve the old behavior, although with various consequences to the CLR process. As an example, we would not be able to use the Dynamic Language Runtime (DLR) and enable assemblies in non-homogenous AppDomains.

There have been several blogs written on this over the years (thanks to Duilio Tacconi from Microsoft Support) to help our community design their reports for performance:

That was a description of the past. Now let’s look at how things work today.

Reporting in Dynamics 365 Business Central

Dynamics 365 Business Central online runs in Windows Azure. We have a new development environment, and we are using a lot of great features in our product, developed and released by many great teams inside and outside of Microsoft. One of the new featuresis used for communication between tiers, (SignalR).

Business Central uses SignalR to communicate with the language service in Visual Studio Code, which is our new development environment, and the backend endpoint in the server. SignalR uses DLR in its implementation,so we turned off the legacy switch described above to do the communication in the development environment. This meant that rendering reports locally on a development machine would be much slower (e.g. REPORT.SAVEASPDF, REPORT.SAVEAS, etc.).

Time was ripe for a rewrite of the server–rendered reports. In the Business Central October’18 update 4 (February 2019), this was all completely rewritten.

Instead of running rendering inthe server’s standard AppDomain, we create a specific Report AppDomain where we can control exactly how marshal the data. In the end, we specify two marshaling operations:

One for the RDLC stream

One for the data stream

This means that we can now control the loading and unloading of the AppDomain and thereby control the performance of trusted reports. At the same time,we can unload the dynamic assemblies that are generated by the rendering, while still the DLR features are still supported.

The Report AppDomain, by default, recycles on every 1000 renderings, which helps avoid piling up too much memory.

This effectively means that if you build RDLC reports, we recommend that you stop using the legacy switch, because it will only revert the server to the behavior that we had in previous releases with slight and controlled memory leaks as consequence.

How to configure the reporting environment

To control how you would like to run your rendering, a set of switches has been introduced. Let’s go through them with a description of what they do together. Most of these switches are internal only, meaning there should be no need to mess with them, but for backwards compatibility reasons, they are still offered.

To use the legacy switch

<NetFx40_LegacySecurityPolicyenabled="true"/>

This switch sets if you want to run in the server’s AppDomain. If you enable the switch, all the new Report AppDomain isolation logic is disregarded. If you set the switch to false or remove it from the config files, the new logic kicks in and you will have fast reports rendered in the Report AppDomain.

To run completely isolated on all renderings

<addkey="LegacyReportAppDomainIsolation"value="true" />

If you add this switch to the CustomSettings.config on the server, you will be able to render reports in the high-security AppDomain, but you will have to set NetFx40_LegacySecurityPolicy to false or remove it completely. This will make all reports run slower.

This switch is not available in the management UI.

To control when to recycle the AppDomain

<addkey="ReportAppDomainRecycleCount"value="1000" />

If you add this switch to the CustomSettings.config on the server and have enabled the new Report AppDomain, you will then be able to influence when the server will try to unload the Report AppDomain. I would not recommend changing this switch, which is why it is declared as internal.

This switch is not available in the management UI.

To control that custom RDLC layouts are trusted

<addkey="ReportAppDomainIsolation"value="true" />

Specifies if application domain isolation is used for rendering custom RDLC layouts. Since custom reports are created by end-users in the client, they can contain some inline Visual Basic .NET code.For that reason, we would enforce secure AppDomain isolation to avoid any security risks from rendering custom RDLC layouts. However, it can considerably increase the time it takes to render reports.

Disabling application domain isolation (<add key=”ReportAppDomainIsolation” value=” false ” /> ) can improve the rendering time but might have a negative impact on security and reliability.

To be secure by default, we recommend that you leave this setting at the default value of true and change it only if you are running an on-premises installation where you can fully trust any custom report layout uploaded by end users.

This does not apply to changes done through C/SIDEcustomization or AL extensions.

NOTE: The name of the switch will change in next major version because the name is not precise enough and we want to avoid confusion.

The new switch will be <addkey=“IsolateCustomReportLayoutsToSecureAppDomain“value=“true“ />

Author:

Torben Wind Meyhoff – Dynamics 365 Business Central Server Architect

We're always looking for feedback and would like to hear from you. Please head to the Dynamics 365 Community to start a discussion, ask questions, and tell us what you think!