Report Sort Ignored After Version Update

1 Answer 57 Views
.NET Framework
Jamie
Top achievements
Rank 1
Jamie asked on 27 Jul 2023, 01:06 PM
We've been using Telerik Reports for years, and have allowed users to set up their own sorting. The below code worked no problem, until we recently upgraded all the way from version Q3 2015 to R1 2023. Now, sorting is never obeyed, despite the parameter on the report getting set - we write out the criteria one the outputted report, and that is correct. We've traced this code through with no errors, and the sortings do seem to be applied to the report object, but they are ignored when the report is built.
 protected override void OnNeedDataSource(object sender, EventArgs e)
        {
            base.OnNeedDataSource(sender, e);

            var report = sender as Telerik.Reporting.Processing.Report;

            //// Report Post Formatting.

            // Apply Sorts.
            if (report.Parameters["Sortings"].Value != null)
            {
                Common.ApplySort(this, report.Parameters["Sortings"].Value.ToString());
            }
}
public static void ApplySort(Report report, string sortingColumns)
        {
            // sorting Columns string format: ColumnName:Asc;ColumnName:Desc
            if (sortingColumns == null)
                return;

            foreach (var item in sortingColumns.Split(','))
            {
                // Get The field value for the column to sort by... IE. SellthroughValuePercent is an iff calculation.
                report.Report.Sortings.Add(new Sorting
                {
                    Expression = string.Format("=Fields.[{0}]", item.Split(':')[0]),
                    Direction = item.Split(':')[1] == "Asc" ? SortDirection.Asc : SortDirection.Desc,
                });
            }
        }
Any ideas to point us in the right direction? It's very frustrating to have bought a new version, but have features that worked before now fail without warning...

1 Answer, 1 is accepted

Sort by
1
Momchil
Telerik team
answered on 01 Aug 2023, 08:52 AM

Hi Jamie,

Thank you for sharing your code.

I believe that the sorting has stopped working for you due to a change made in R3 2016. In short, an optimization to the report rendering was made by caching report item definition properties. This led to a notable performance improvement of up to 25%, but it caused some report modifications made within Report Events to break.

The above is explained further in the Editing Report Items in Report Events Does Not Take Effect Knowledge Base article along with a couple of workarounds.

That said, considering the information from the article and the code you shared, I think that the following is the source of the issue. The ApplySort method accepts a Telerik.Reporting.Report as an argument but since the report is modified within a Report Event, the changes are not guaranteed to take effect. Thus, you can try disabling the cacheDefinitionProperties option.

I hope this helps.

Regards,
Momchil
Progress Telerik

Stay tuned by visiting our roadmap and feedback portal pages, enjoy a smooth take-off with our Getting Started resources, or visit the free self-paced technical training at https://learn.telerik.com/.
Jamie
Top achievements
Rank 1
commented on 01 Aug 2023, 11:37 AM

Thanks for the answer - it does sound reasonable, but setting that property as mentioned in the article doesn't seem to have any effect. Is there any way to check during run time whether that is turned off correctly? Or any other possibilities to try?
Momchil
Telerik team
commented on 03 Aug 2023, 03:06 PM

To check whether the property has been disabled correctly you can use the ConfigurationManager.GetSection(String) method. For example:

That said, other sorting alternatives I can suggest are:

1. Moving the existing code outside of the OnNeedDataSource report event. For example, if your application is using a Reporting REST Service, you could use a Custom ReportSource Resolver.

public class CustomReportSourceResolver : IReportSourceResolver
{
    private readonly string repositoryDirectory;

    public CustomReportSourceResolver(string repositoryDirectory)
    {
        this.repositoryDirectory = repositoryDirectory;
    }

    public ReportSource Resolve(string reportID, OperationOrigin operationOrigin, IDictionary<string, object> currentParameterValues)
    {
        var reportPackager = new ReportPackager();
        Telerik.Reporting.Report report = null;
        using (var sourceStream = System.IO.File.OpenRead(repositoryDirectory + "\\" + reportID))
        {
             report = (Report)reportPackager.UnpackageDocument(sourceStream);
        }

        if (operationOrigin != OperationOrigin.ResolveReportParameters) 
        {
            var sortingColumns = currentParameterValues["Sortings"].ToString();
            foreach (var item in sortingColumns.Split(','))
            {
                report.Report.Sortings.Add(new Sorting
                {
                    Expression = string.Format("=Fields.[{0}]", item.Split(':')[0]),
                    Direction = item.Split(':')[1] == "Asc" ? SortDirection.Asc : SortDirection.Desc,
                });
            }
        }

        return new InstanceReportSource() { ReportDocument = report };
    }
}

2. Use an ObjectDataSource, pass the Sortings parameter to it (see Using Parameters with ObjectDataSource), and apply the sorting on the data source level.

3. Create a separate report parameter for each sorting rule and add the corresponding report sorting using expressions on the report definition level.

4. Use the Sorting Action if it applies to your scenario.

Jamie
Top achievements
Rank 1
commented on 17 Oct 2023, 02:52 PM

It's taken a while due to other business, but trying some of these solutions hasn't gotten us further forward. ConfigurationManager.GetSection(String)  does state that the cacheDefinitionProperties option is turned off, and in fact it's disabled both in the settings file and on the individual report.

All parameters function correctly except for sorting applied in the OnNeedDataSource event (as shown in the opening post). The CustomReportSourceResolver sounded like a reasonable idea, but we use TypeReportSourceResolver currently and there doesn't seem to be a way to exchange one for the other without substantial changes. The other options sound similarly substantial changes, and we have a lot of reports currently. 

I can happily share more code if it's of any use - the entire project is too big, but if there's any areas it would be useful to see that can be arranged.

Momchil
Telerik team
commented on 20 Oct 2023, 01:36 PM

I understand that exchanging the report source resolver mechanism can require a lot of changes when working on a large project.

However, perhaps you do not need to exchange it entirely. I say that because the built-in ReportSource Resolver implementations have a fallback mechanism (see AddFallbackResolver).

Since you are using the TypeReportSourceResolver, you can add your CustomReportSourceResolver as a fallback mechanism.

ReportSourceResolver = new TypeReportSourceResolver()
    .AddFallbackResolver(
        new CustomReportSourceResolver())

In order for the reports to fall back to your custom resolver, the identifier passed from the viewer needs to be considered invalid by the TypeReportSourceResolver. For example, if you used to pass the assembly-qualified name of a report, you can pass "CUSTOM_RESOLVER-{assembly-qualified name}".

Additionally, the custom resolver does not need to work with physical report definitions as shown in my previous example. You can use the modified assembly-qualified name passed from the viewer to create an instance of the corresponding report class, modify it directly, and return the modified instance wrapped in an InstanceReportSource.

In theory, this approach should enable you to modify only the problematic reports without affecting all other reports.

Jamie
Top achievements
Rank 1
commented on 25 Oct 2023, 10:09 AM

Thanks, we think that has resolved the issue - we were actually able to do it generically with the custom report resolver, as below. No fallback necessary and not seen any problems yet.
 public ReportSource Resolve(string reportID, OperationOrigin operationOrigin, IDictionary<string, object> currentParameterValues)
 {
     Type ty = Type.GetType(reportID);
     object repo = Activator.CreateInstance(ty);

     Telerik.Reporting.Report report = (Report)repo;

//sorting code

     return new InstanceReportSource() { ReportDocument = report };

}

     

Tags
.NET Framework
Asked by
Jamie
Top achievements
Rank 1
Answers by
Momchil
Telerik team
Share this question
or