New to Telerik ReportingStart a free 30-day trial

How to render reports asynchronously by using the async and await keywords

Environment

ProductProgress® Telerik® Reporting
Framework.NET Framework 4.5 and higher

Description

.NET Framework 4.5 introduced the Task<T> class and the async and await keywords, which simplify parallel programming. This article explains how to use these features with the Telerik Reporting ReportProcessor class to render reports asynchronously, and how to pass report parameters to reports that require them.

Solution

The ReportProcessor class and its RenderReport method provide all the functionality needed for programmatic report generation. Because RenderReport returns a RenderingResult, wrap the call in a method that returns Task<RenderingResult> to use it asynchronously:

csharp
public class AsyncWrappers
{
    // Wrap the RenderingResult like this:
    public async Task<RenderingResult> RenderReportAsync(Type reportType) // Pass parameters here, like device info and report to render
    {
        ReportProcessor reportProcessor = new ReportProcessor();
 
        // Apply any deviceInfo settings if necessary
        Hashtable deviceInfo = new Hashtable();
 
        // Any other Report Source can be used instead
        // For example InstanceReportSource can be used if the instantiated report is passed as parameter of the method
        TypeReportSource typeReportSource = new TypeReportSource();
 
        typeReportSource.TypeName = reportType.AssemblyQualifiedName;
 
        return await Task.Run(() => reportProcessor.RenderReport("PDF", typeReportSource, deviceInfo));
    }
}

The method returns Task<RenderingResult> and is marked async. The RenderReport call is wrapped in Task.Run and awaited, offloading the blocking render work to a thread-pool thread. Use the wrapper in your application code:

csharp
//Use the wrapper in your code like this:
public async Task<string> RenderReportAsync()
{
    var asyncWrappers = new AsyncWrappers();
  
    Console.WriteLine("Rendering started on: {0}", Thread.CurrentThread.ManagedThreadId);
  
    var result = await asyncWrappers.RenderReportAsync( typeof(Telerik.Reporting.Report) );// pass the type of your report here
  
    string fileName = result.DocumentName + "." + result.Extension;
    string path = System.IO.Path.GetTempPath();
    string filePath = System.IO.Path.Combine(path, fileName);
  
    using (System.IO.FileStream fs = new System.IO.FileStream(filePath, System.IO.FileMode.Create))
    {
        fs.Write(result.DocumentBytes, 0, result.DocumentBytes.Length);
    }
  
    Console.WriteLine("Rendering finished on: {0}\n", Thread.CurrentThread.ManagedThreadId);
  
    // You can return void but that is not recommended
    return string.Format("Successfully rendered! File saved in {0}\n", filePath);
}

Full Console Application Example

The following example invokes the wrapper from a console application. While the report renders on a background thread, the main thread continues executing Count():

C#
class Program
{
    // A working example
    static void Main(string[] args)
    {
        // Use your class to render the report in an async manner
        var myClass = new MyClass();
  
        Console.WriteLine("Starting rendering...");
        var task = myClass.RenderReportAsync();
  
        // While the report is rendering do something else
        Count();
  
        Console.WriteLine("Main finished on: {0}", Thread.CurrentThread.ManagedThreadId);
        Console.WriteLine("Render Status: {0}\n", task.Result);
  
        Console.ReadLine();
    }
  
    static void Count()
    {
        for (int i = 0; i < 10; i++)
        {
            Console.WriteLine(i);
        }
    }
}

Passing Report Parameters

Some reports define parameters that must be supplied before rendering. The TypeReportSource class exposes a Parameters collection where you add Parameter objects.

Extend the wrapper method to accept a parameter dictionary, then populate the collection before the Task.Run call:

csharp
public class AsyncWrappers
{
    public async Task<RenderingResult> RenderReportAsync(
        Type reportType,
        IDictionary<string, object> parameters = null)
    {
        ReportProcessor reportProcessor = new ReportProcessor();
        Hashtable deviceInfo = new Hashtable();

        TypeReportSource typeReportSource = new TypeReportSource();
        typeReportSource.TypeName = reportType.AssemblyQualifiedName;

        if (parameters != null)
        {
            foreach (var param in parameters)
            {
                typeReportSource.Parameters.Add(new Parameter(param.Key, param.Value));
            }
        }

        return await Task.Run(() => reportProcessor.RenderReport("PDF", typeReportSource, deviceInfo));
    }
}

Call the extended wrapper with a parameter dictionary:

csharp
var result = await asyncWrappers.RenderReportAsync(
    typeof(MyReport),
    new Dictionary<string, object>
    {
        { "StartDate", new DateTime(2024, 1, 1) },
        { "EndDate", new DateTime(2024, 12, 31) }
    });

For reports that need direct access to the report instance — for example, to set properties in addition to parameters — use InstanceReportSource instead:

csharp
var report = new MyReport(); // your report class
report.ReportParameters["StartDate"].Value = new DateTime(2024, 1, 1);
report.ReportParameters["EndDate"].Value = new DateTime(2024, 12, 31);

InstanceReportSource instanceReportSource = new InstanceReportSource();
instanceReportSource.ReportDocument = report;

return await Task.Run(() => reportProcessor.RenderReport("PDF", instanceReportSource, deviceInfo));

If a report has no parameters, the simplified wrapper from the first example works without modification.

Notes

Download a sample console application.

See Also

Embedded Report Engine