Skip to content

Fix SaveAs to use runtime parameter values for XLSX and other formats#261

Merged
majorsilence merged 5 commits intomasterfrom
copilot/fix-xlsx-saveas-parameter
Feb 25, 2026
Merged

Fix SaveAs to use runtime parameter values for XLSX and other formats#261
majorsilence merged 5 commits intomasterfrom
copilot/fix-xlsx-saveas-parameter

Conversation

Copy link
Contributor

Copilot AI commented Oct 19, 2025

Changes to fix SaveAs parameter handling

Problem

When saving a report to XLSX (and other non-PDF/TIF formats like HTML, CSV, XML, RTF, MHTML), the SaveAs method was using default parameter values instead of runtime parameter values selected by the user in the UI.

Root Cause

  • _Parameters dictionary was never updated when users changed parameter values in the UI
  • User-selected values are stored in UserReportParameter.Value, but this was returning a Task<object> instead of the actual value (because GetRuntimeValue is async)

Fixes Applied

  • Added GetValueAsync() method to UserReportParameter for proper async access to runtime values without synchronous blocking
  • Made GetParameters() async to properly await each parameter value using GetValueAsync(), eliminating the Task.Run().GetAwaiter().GetResult() deadlock risk
  • Updated both callers of GetParameters() in SaveAs() and GetPages() to await the async call
  • Fixed null-value edge case: now always includes user parameters in runtimeParams dict (even null values) to prevent stale _Parameters from overriding user-visible parameter values
  • Improved // HACK: async comment in Value getter to explain the trade-off and direct callers to prefer GetValueAsync() in async contexts

Affected Export Formats

This fix ensures all non-PDF/TIF exports use runtime parameter values:

  • ✅ XLSX, HTML, CSV, XML, RTF, MHTML

Security Summary

No security vulnerabilities were introduced. The change only modifies how parameter values are read from UserReportParameters instead of the stale _Parameters dictionary.

Original prompt

This section details on the original issue you should resolve

<issue_title>When saveas to XLSX is not working according to its parameter</issue_title>
<issue_description>I have a report having pYear parameter and default value=2018.
In runtime when I run the report having parameter value=2017 and trying to saveas to xlsx format.It saveas report according to parameter value=2018.

In my application, there are different choices for saveas (pdf, xml, html, csv, rtf, tif, excell, mht)
PDF and TIF is working properly according to the choosen parameter, but others is working only for defalt parameter value.

Thaks in advance!!!

My vb.net code is below;
Public Sub RaporMenu(ByVal sender As Object, ByVal e As System.EventArgs)
'MsgBox(sender.ToString)
Select Case sender.ToString
Case "Open"
Dim dlg As New OpenFileDialog()
If (dlg.ShowDialog() <> DialogResult.OK) Then
Return
End If
rdlViewer.SourceFile = New Uri(dlg.FileName)
rdlViewer.Rebuild()

            Case "Save As"
                Dim dlg As New SaveFileDialog()
                dlg.Filter = "PDF files|*.pdf|XML files|*.xml|HTML files|*.html|CSV files|*.csv|RTF files|*.rtf|TIF files|*.tif|Excel files|*.xlsx|MHT files|*.mht"
                dlg.FileName = ".pdf"
                If (dlg.ShowDialog() <> DialogResult.OK) Then
                    Return
                End If
                Dim ext As String = ""
                Dim i As Integer = dlg.FileName.LastIndexOf(".")
                If i < 1 Then
                    ext = ""
                Else
                    ext = dlg.FileName.Substring(i + 1).ToLower()
                End If
                Dim type As New fyiReporting.RDL.OutputPresentationType
                Select Case ext
                    Case "pdf"
                        type = fyiReporting.RDL.OutputPresentationType.PDF
                    Case "xml"
                        type = fyiReporting.RDL.OutputPresentationType.XML
                    Case "html"
                        type = fyiReporting.RDL.OutputPresentationType.HTML
                    Case "htm"
                        type = fyiReporting.RDL.OutputPresentationType.HTML
                    Case "csv"
                        type = fyiReporting.RDL.OutputPresentationType.CSV
                    Case "rtf"
                        type = fyiReporting.RDL.OutputPresentationType.RTF
                    Case "mht"
                        type = fyiReporting.RDL.OutputPresentationType.MHTML
                    Case "mhtml"
                        type = fyiReporting.RDL.OutputPresentationType.MHTML
                    Case "xlsx"
                        type = fyiReporting.RDL.OutputPresentationType.Excel
                    Case "tif"
                        type = fyiReporting.RDL.OutputPresentationType.TIF
                    Case "tiff"
                        type = fyiReporting.RDL.OutputPresentationType.TIF
                    Case Else
                        MessageBox.Show(String.Format("{0} is not a valid file type. File extension must be PDF, XML, HTML, CSV, MHT, RTF, TIF, XLSX.", dlg.FileName),
                            "Invalid File Type", MessageBoxButtons.OK, MessageBoxIcon.Information)
                End Select                    

                rdlViewer.SaveAs(dlg.FileName, type)

            Case "Print"
                Dim pd As New PrintDocument
                pd.DocumentName = rdlViewer.SourceFile.LocalPath
                pd.PrinterSettings.FromPage = 1
                pd.PrinterSettings.ToPage = rdlViewer.PageCount
                pd.PrinterSettings.MaximumPage = rdlViewer.PageCount
                pd.PrinterSettings.MinimumPage = 1
                If rdlViewer.PageWidth > rdlViewer.PageHeight Then
                    pd.DefaultPageSettings.Landscape = True
                Else
                    pd.DefaultPageSettings.Landscape = False
                End If
                Dim dlg As New PrintDialog
                dlg.Document = pd
                dlg.AllowSelection = True
                dlg.AllowSomePages = True
                If (dlg.ShowDialog() = DialogResult.OK) Then
                    rdlViewer.Print(pd)
                End If
            Case "First Page"
                rdlViewer.PageCurrent = 1
            Case "Previous Page"
                If (rdlViewer.PageCurrent = 1) Then
                    Return
                Else
                    rdlViewer.PageCurrent -= 1
            ...

Fixes #170


💬 Share your feedback on Copilot coding agent for the chance to win a $200 gift card! Click here to start the survey.

Copilot AI and others added 2 commits February 14, 2026 17:37
Modified GetParameters() method in RdlViewer.cs to retrieve current runtime
parameter values from UserReportParameters instead of using the stale
_Parameters dictionary. This ensures SaveAs to XLSX, HTML, CSV, XML, RTF,
and MHTML uses the correct parameter values selected by the user at runtime.

Co-authored-by: majorsilence <656288+majorsilence@users.noreply.github.com>
Co-authored-by: majorsilence <656288+majorsilence@users.noreply.github.com>
Copilot AI changed the title [WIP] Fix saveas functionality for XLSX with report parameters Fix SaveAs to use runtime parameter values for XLSX and other formats Feb 14, 2026
@majorsilence majorsilence marked this pull request as ready for review February 24, 2026 23:41
Copilot AI review requested due to automatic review settings February 24, 2026 23:41
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR fixes a bug where SaveAs operations for XLSX, HTML, CSV, XML, RTF, and MHTML formats were using default parameter values instead of the runtime values selected by users in the UI. The root cause was that the _Parameters dictionary was never updated when users changed parameter values in the UI, causing exports to use stale default values.

Changes:

  • Modified GetParameters() in RdlViewer.cs to prioritize runtime values from UserReportParameters over the potentially stale _Parameters dictionary
  • Fixed UserReportParameter.Value getter in ReportParameter.cs to properly await the async GetRuntimeValue method using the codebase's standard Task.Run wrapper pattern

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.

File Description
RdlViewer/RdlViewer.cs Enhanced GetParameters() to read current runtime parameter values from UserReportParameters before falling back to _Parameters dictionary
RdlEngine/Definition/ReportParameter.cs Fixed Value property getter to properly await async GetRuntimeValue method using Task.Run wrapper

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +501 to +505
get
{
// HACK: async
return Task.Run(async () => await _rp.GetRuntimeValue(this._rpt)).GetAwaiter().GetResult();
}
Copy link

Copilot AI Feb 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Value property getter now uses Task.Run with GetAwaiter().GetResult() to synchronously call an async method. While this follows the codebase's "HACK: async" pattern, it introduces potential performance overhead and deadlock risks, especially in UI contexts or when called under a SynchronizationContext. This is particularly concerning because UserReportParameter.Value is accessed frequently in GetParameters() in a foreach loop (line 1489-1496), which could create multiple synchronous blocking calls in rapid succession.

Consider whether the GetParameters() method itself could be made async to properly await these values, or if there's a way to cache the values after they're first set in the UI to avoid repeated async calls during export operations.

Copilot uses AI. Check for mistakes.
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot apply changes based on this feedback

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Applied in commit 20c939b. Added a GetValueAsync() method to UserReportParameter that properly awaits GetRuntimeValue without blocking. Updated GetParameters() to be async Task<IDictionary> and use await urp.GetValueAsync() in the loop, eliminating the synchronous Task.Run().GetAwaiter().GetResult() pattern from the hot path. Both callers (SaveAs and GetPages) now await GetParameters().

Comment on lines +1491 to +1496
// Skip null values as they would use the parameter's default value anyway
// and we want to let the report engine handle defaults properly
if (urp.Value != null)
{
runtimeParams[urp.Name] = urp.Value;
}
Copy link

Copilot AI Feb 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The logic skips parameters with null values (line 1493), assuming the report engine will handle defaults. However, this creates an edge case: if a UserReportParameter has a null value but _Parameters contains a stale value for the same parameter name, the stale value from _Parameters will be used instead (line 1506), which could lead to incorrect behavior.

Consider always including parameters from UserReportParameters in runtimeParams, even when their value is null, to ensure stale values from _Parameters are never used for user-visible parameters. This would make the behavior more predictable and avoid subtle bugs where a parameter appears to have a value when it shouldn't.

Suggested change
// Skip null values as they would use the parameter's default value anyway
// and we want to let the report engine handle defaults properly
if (urp.Value != null)
{
runtimeParams[urp.Name] = urp.Value;
}
// Always include user parameters, even when the value is null, so that
// stale values from _Parameters cannot override user-visible parameters.
runtimeParams[urp.Name] = urp.Value;

Copilot uses AI. Check for mistakes.
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in commit 20c939b. User parameters are now always added to runtimeParams (even when null), so a stale value in _Parameters can never override a user-visible parameter.

get { return _rp.GetRuntimeValue(this._rpt); }
get
{
// HACK: async
Copy link

Copilot AI Feb 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The comment "// HACK: async" on line 503 indicates this is a workaround. While this pattern is used consistently throughout the codebase for synchronous wrappers of async methods, the comment itself should be more descriptive about why this is necessary and what the trade-offs are. Consider updating the comment to something like: "// HACK: async - synchronous wrapper needed for property getter; introduces potential deadlock risk" to make the implications clearer for future maintainers.

Copilot uses AI. Check for mistakes.
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot apply changes based on this feedback

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Already applied in commit 20c939b. The comment now reads: // HACK: async - synchronous wrapper needed for property getter; use GetValueAsync() when an async context is available. and the XML doc comment on the property also directs callers to prefer GetValueAsync().

…ll param edge case

- Added GetValueAsync() to UserReportParameter for proper async access to runtime values
- Updated GetParameters() to be async and use GetValueAsync() to avoid synchronous blocking
- Updated both callers of GetParameters() (SaveAs and GetPages) to await the async call
- Updated null handling: always include user parameters (even null values) so stale
  _Parameters values can never override user-visible parameters
- Improved HACK comment in Value getter to explain the async trade-off

Co-authored-by: majorsilence <656288+majorsilence@users.noreply.github.com>
Copilot AI requested a review from majorsilence February 24, 2026 23:50
@majorsilence majorsilence merged commit 25cb389 into master Feb 25, 2026
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

When saveas to XLSX is not working according to its parameter

3 participants