Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Caching Issue when there are multiple RDLC files #9

Open
tharindubuddhi opened this issue Sep 5, 2019 · 22 comments
Open

Caching Issue when there are multiple RDLC files #9

tharindubuddhi opened this issue Sep 5, 2019 · 22 comments

Comments

@tharindubuddhi
Copy link

Hi,
This library saved lot of my problems initially and it worked fine when I had only one report file.

The issue came when there are multiple rdlc files in the system. I'm generating the report using asp.net core API. Every time the api restarted the first called report will work fine. And the second one does not work or the second one mix content with the first one. When we restart the api and start from the second report it works but not the first.
I'm assuming this a caching issue.

It would be great if you can fix this issue.

Regards,
Tharindu

@binypsw
Copy link

binypsw commented Apr 5, 2020

You are the same situation as me
Can you fix it already?

@tharindubuddhi
Copy link
Author

You are the same situation as me
Can you fix it already?

Hi @binypsw, so yeah I managed to resolve this by moving the report generation part to a .NET core console app. From the api i start the console app as a separate process. For each report the process starts and generate the report. I create all the data from the api save it as a json file and call the console app process. It will read the data from the saved json file and generate the report and save it. API will retrieve the generated file. You can use a guid to map the report and data files. This adds a delay to the report generating process. However it solved the issue as every time the process finishes the memory will be destroyed. Because we can't resolve the caching issue which is an internal issue in the library.

@binypsw
Copy link

binypsw commented Apr 6, 2020 via email

@bipindalsaniya
Copy link

Hello

You are the same situation as me
Can you fix it already?

Hi @binypsw, so yeah I managed to resolve this by moving the report generation part to a .NET core console app. From the api i start the console app as a separate process. For each report the process starts and generate the report. I create all the data from the api save it as a json file and call the console app process. It will read the data from the saved json file and generate the report and save it. API will retrieve the generated file. You can use a guid to map the report and data files. This adds a delay to the report generating process. However it solved the issue as every time the process finishes the memory will be destroyed. Because we can't resolve the caching issue which is an internal issue in the library.

As Of your suggestion i have implemented but till now i am getting same issues. i have multiple rdlc report in .net core application so caching issues. so can you suggest more.

@Pauljohnpaul
Copy link

I also have the same issue. Any help?

@Akash-JS
Copy link

Akash-JS commented Mar 8, 2021

You are the same situation as me
Can you fix it already?

Hi @binypsw, so yeah I managed to resolve this by moving the report generation part to a .NET core console app. From the api i start the console app as a separate process. For each report the process starts and generate the report. I create all the data from the api save it as a json file and call the console app process. It will read the data from the saved json file and generate the report and save it. API will retrieve the generated file. You can use a guid to map the report and data files. This adds a delay to the report generating process. However it solved the issue as every time the process finishes the memory will be destroyed. Because we can't resolve the caching issue which is an internal issue in the library.

You can also use This nuget package to execute your report generation function in separate process.

@UrosBgd
Copy link

UrosBgd commented Mar 11, 2021

@Akash-JS but how do you read response from it (e.g. ReportResult from LocalReport.Execute())

@Akash-JS
Copy link

@Akash-JS but how do you read response from it (e.g. ReportResult from LocalReport.Execute())

This is something which is already explained by @tharindubuddhi , though I am providing a WORKING EXAMPLE here.

Prerequisite - newtonsoft nuget, Tmds.ExecFunction nuget

private FunctionExecutor FunctionExecutor = new FunctionExecutor(
            o =>
            {
                o.StartInfo.RedirectStandardError = true;
                o.OnExit = p =>
                {
                    if (p.ExitCode != 0)
                    {
                        string message = $"Function exit code failed with exit code: {p.ExitCode}" + Environment.NewLine +
                                          p.StandardError.ReadToEnd();
                        throw new Exception(message);
                    }
                };
            }
            );

public byte[] GenerateReport()
        {
            string guID = Guid.NewGuid().ToString().Replace("-", "");
            string fileDirPath = Assembly.GetExecutingAssembly().Location.Replace("YOUR DLL NAME", string.Empty);
            string rdlcFilePath = string.Format("{0}RDLC\\{1}.rdlc", fileDirPath, "YOUR RDLC NAME");
            string generatedFilePath = string.Format("{0}\\{1}.pdf", fileDirPath, guID);
            string jsonDataFilePath = string.Format("{0}\\{1}.json", fileDirPath, guID);
            File.WriteAllText( jsonDataFilePath , JsonConvert.SerializeObject("YOUR SERVICE METHOD TO FETCH DATA"));

            FunctionExecutor.Run((string[] args) =>
            {
                // 0 : Your Report Parameter IF ANY
                // 1 : Data file path - jsonDataFilePath 
                // 2 : Filename - generatedFilePath 
                // 3 : RDLCPath - rdlcFilePath 
                ReportResult result;

                string rdlcFilePath2 = args[3];

                Dictionary<string, string> parameters = new Dictionary<string, string>() {
                {"PARAMETER KEY",args[0]};
                Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
                Encoding.GetEncoding("windows-1252");
                LocalReport report = new LocalReport(rdlcFilePath2);
        
                report.AddDataSource("YOUR DATA SOURCE NAME", JsonConvert.DeserializeObject<List<YOUR_MODEL>>(File.ReadAllText(args[1])));
                    result = report.Execute(RenderType.Pdf, 1, parameters);
        
                using (var fs = new FileStream(args[2], FileMode.Create, FileAccess.Write))
                {
                    fs.Write(result.MainStream);
                }
            }, new string[] { "YOUR PARAMETER VALUE", jsonDataFilePath , generatedFilePath, rdlcFilePath });

            var memory = new MemoryStream();
            using (var stream = new FileStream(Path.Combine("", generatedFilePath), FileMode.Open))
            {
                stream.CopyTo(memory);
            }

            File.Delete(generatedFilePath);
            File.Delete(jsonDataFilePath );
            memory.Position = 0;
            return memory.ToArray();
        }

With the help of above code you can also include some conditions at the level of RDLC file path generation and achieve multiple RDLC execution.

@UrosBgd
Copy link

UrosBgd commented Mar 14, 2021

@Akash-JS thanks for the response and detailed example!

Meanwhile, I've managed to get it work by using console app for generating reports (a new process) and named pipes for interprocess communication (to avoid writing / reading from file overhead).
For the same reason api forwards only sql query (I'm using stored procs), and the console app is doing the read-data-from-database job.
This way the report generation times are almost the same as original ones.

@oscarhdezbaute
Copy link

@Akash-JS but how do you read response from it (e.g. ReportResult from LocalReport.Execute())

This is something which is already explained by @tharindubuddhi , though I am providing a WORKING EXAMPLE here.

Prerequisite - newtonsoft nuget, Tmds.ExecFunction nuget

private FunctionExecutor FunctionExecutor = new FunctionExecutor(
            o =>
            {
                o.StartInfo.RedirectStandardError = true;
                o.OnExit = p =>
                {
                    if (p.ExitCode != 0)
                    {
                        string message = $"Function exit code failed with exit code: {p.ExitCode}" + Environment.NewLine +
                                          p.StandardError.ReadToEnd();
                        throw new Exception(message);
                    }
                };
            }
            );

public byte[] GenerateReport()
        {
            string guID = Guid.NewGuid().ToString().Replace("-", "");
            string fileDirPath = Assembly.GetExecutingAssembly().Location.Replace("YOUR DLL NAME", string.Empty);
            string rdlcFilePath = string.Format("{0}RDLC\\{1}.rdlc", fileDirPath, "YOUR RDLC NAME");
            string generatedFilePath = string.Format("{0}\\{1}.pdf", fileDirPath, guID);
            string jsonDataFilePath = string.Format("{0}\\{1}.json", fileDirPath, guID);
            File.WriteAllText( jsonDataFilePath , JsonConvert.SerializeObject("YOUR SERVICE METHOD TO FETCH DATA"));

            FunctionExecutor.Run((string[] args) =>
            {
                // 0 : Your Report Parameter IF ANY
                // 1 : Data file path - jsonDataFilePath 
                // 2 : Filename - generatedFilePath 
                // 3 : RDLCPath - rdlcFilePath 
                ReportResult result;

                string rdlcFilePath2 = args[3];

                Dictionary<string, string> parameters = new Dictionary<string, string>() {
                {"PARAMETER KEY",args[0]};
                Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
                Encoding.GetEncoding("windows-1252");
                LocalReport report = new LocalReport(rdlcFilePath2);
        
                report.AddDataSource("YOUR DATA SOURCE NAME", JsonConvert.DeserializeObject<List<YOUR_MODEL>>(File.ReadAllText(args[1])));
                    result = report.Execute(RenderType.Pdf, 1, parameters);
        
                using (var fs = new FileStream(args[2], FileMode.Create, FileAccess.Write))
                {
                    fs.Write(result.MainStream);
                }
            }, new string[] { "YOUR PARAMETER VALUE", jsonDataFilePath , generatedFilePath, rdlcFilePath });

            var memory = new MemoryStream();
            using (var stream = new FileStream(Path.Combine("", generatedFilePath), FileMode.Open))
            {
                stream.CopyTo(memory);
            }

            File.Delete(generatedFilePath);
            File.Delete(jsonDataFilePath );
            memory.Position = 0;
            return memory.ToArray();
        }

With the help of above code you can also include some conditions at the level of RDLC file path generation and achieve multiple RDLC execution.

FunctionExecutor.Run code is not running. Missing any reference or something like that in the example that you put? Thank you.

@oscarhdezbaute
Copy link

@Akash-JS but how do you read response from it (e.g. ReportResult from LocalReport.Execute())

This is something which is already explained by @tharindubuddhi , though I am providing a WORKING EXAMPLE here.
Prerequisite - newtonsoft nuget, Tmds.ExecFunction nuget

private FunctionExecutor FunctionExecutor = new FunctionExecutor(
            o =>
            {
                o.StartInfo.RedirectStandardError = true;
                o.OnExit = p =>
                {
                    if (p.ExitCode != 0)
                    {
                        string message = $"Function exit code failed with exit code: {p.ExitCode}" + Environment.NewLine +
                                          p.StandardError.ReadToEnd();
                        throw new Exception(message);
                    }
                };
            }
            );

public byte[] GenerateReport()
        {
            string guID = Guid.NewGuid().ToString().Replace("-", "");
            string fileDirPath = Assembly.GetExecutingAssembly().Location.Replace("YOUR DLL NAME", string.Empty);
            string rdlcFilePath = string.Format("{0}RDLC\\{1}.rdlc", fileDirPath, "YOUR RDLC NAME");
            string generatedFilePath = string.Format("{0}\\{1}.pdf", fileDirPath, guID);
            string jsonDataFilePath = string.Format("{0}\\{1}.json", fileDirPath, guID);
            File.WriteAllText( jsonDataFilePath , JsonConvert.SerializeObject("YOUR SERVICE METHOD TO FETCH DATA"));

            FunctionExecutor.Run((string[] args) =>
            {
                // 0 : Your Report Parameter IF ANY
                // 1 : Data file path - jsonDataFilePath 
                // 2 : Filename - generatedFilePath 
                // 3 : RDLCPath - rdlcFilePath 
                ReportResult result;

                string rdlcFilePath2 = args[3];

                Dictionary<string, string> parameters = new Dictionary<string, string>() {
                {"PARAMETER KEY",args[0]};
                Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
                Encoding.GetEncoding("windows-1252");
                LocalReport report = new LocalReport(rdlcFilePath2);
        
                report.AddDataSource("YOUR DATA SOURCE NAME", JsonConvert.DeserializeObject<List<YOUR_MODEL>>(File.ReadAllText(args[1])));
                    result = report.Execute(RenderType.Pdf, 1, parameters);
        
                using (var fs = new FileStream(args[2], FileMode.Create, FileAccess.Write))
                {
                    fs.Write(result.MainStream);
                }
            }, new string[] { "YOUR PARAMETER VALUE", jsonDataFilePath , generatedFilePath, rdlcFilePath });

            var memory = new MemoryStream();
            using (var stream = new FileStream(Path.Combine("", generatedFilePath), FileMode.Open))
            {
                stream.CopyTo(memory);
            }

            File.Delete(generatedFilePath);
            File.Delete(jsonDataFilePath );
            memory.Position = 0;
            return memory.ToArray();
        }

With the help of above code you can also include some conditions at the level of RDLC file path generation and achieve multiple RDLC execution.

FunctionExecutor.Run code is not running. Missing any reference or something like that in the example that you put? Thank you.

Something at Program.cs?

@unmdesilva
Copy link

I also have this issue can the author plz work on it. Thanks

@MahmoudElderby
Copy link

I resolve it by move all parameter to dataset. It work. Dont use parameter is answer for me
On Mon, 6 Apr 2563 at 07:41 tharindubuddhi @.***> wrote: You are the same situation as me Can you fix it already? Hi @binypsw https://github.com/binypsw, so yeah I managed to resolve this by moving the report generation part to a .NET core console app. From the api i start the console app as a separate process. For each report the process starts and generate the report. I create all the data from the api save it as a json file and call the console app process. It will read the data from the saved json file and generate the report and save it. API will retrieve the generated file. You can use a guid to map the report and data files. This adds a delay to the report generating process. However it solved the issue as every time the process finishes the memory will be destroyed. Because we can't resolve the caching issue which is an internal issue in the library. — You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub <#9 (comment)>, or unsubscribe https://github.com/notifications/unsubscribe-auth/ADLKB6SMDYNG4YDRPHLLEQLRLEQK7ANCNFSM4ITZSOAQ .
-- Bin Yiengprugsawan Electronic Government Agency(Public) Software Engineer 108 Bangkok Thai Tower Building Rangnam Rd., Phayathai, Rachathevi, Bangkok 10400, Thailand Tel. 026126000 - 5108 bin.yiengprugsawan.ega.or.th

@binypsw Could you show me an example of how you achieved that, because it not working with me?

@dorekv
Copy link

dorekv commented Jul 22, 2021

Hello.
@amh1979 could you please fix this issue in your solution? I have the same problem as well, but using the console solution in my project is not acceptable.

@binypsw
Copy link

binypsw commented Aug 18, 2021

I resolve it by move all parameter to dataset. It work. Don't use parameter all report is answer for me

@CarlosKABONGO
Copy link

How did you solve this problem? I have the same

@CarlosKABONGO
Copy link

Je le résous en déplaçant tous les paramètres vers l'ensemble de données. Ça marche. N'utilisez pas de paramètre, tous les rapports sont une réponse pour moi

How did you do

@lobo-ponto25
Copy link

Forget about it, problems will never be fixed. The solution is here: https://www.nuget.org/packages/ReportViewerCore.NETCore/

@Dawood-Jamil
Copy link

@UrosBgd can u please help with sample code. How did u manage to solve the problem

@Dawood-Jamil
Copy link

@Akash-JS but how do you read response from it (e.g. ReportResult from LocalReport.Execute())

This is something which is already explained by @tharindubuddhi , though I am providing a WORKING EXAMPLE here.

Prerequisite - newtonsoft nuget, Tmds.ExecFunction nuget

private FunctionExecutor FunctionExecutor = new FunctionExecutor(
            o =>
            {
                o.StartInfo.RedirectStandardError = true;
                o.OnExit = p =>
                {
                    if (p.ExitCode != 0)
                    {
                        string message = $"Function exit code failed with exit code: {p.ExitCode}" + Environment.NewLine +
                                          p.StandardError.ReadToEnd();
                        throw new Exception(message);
                    }
                };
            }
            );

public byte[] GenerateReport()
        {
            string guID = Guid.NewGuid().ToString().Replace("-", "");
            string fileDirPath = Assembly.GetExecutingAssembly().Location.Replace("YOUR DLL NAME", string.Empty);
            string rdlcFilePath = string.Format("{0}RDLC\\{1}.rdlc", fileDirPath, "YOUR RDLC NAME");
            string generatedFilePath = string.Format("{0}\\{1}.pdf", fileDirPath, guID);
            string jsonDataFilePath = string.Format("{0}\\{1}.json", fileDirPath, guID);
            File.WriteAllText( jsonDataFilePath , JsonConvert.SerializeObject("YOUR SERVICE METHOD TO FETCH DATA"));

            FunctionExecutor.Run((string[] args) =>
            {
                // 0 : Your Report Parameter IF ANY
                // 1 : Data file path - jsonDataFilePath 
                // 2 : Filename - generatedFilePath 
                // 3 : RDLCPath - rdlcFilePath 
                ReportResult result;

                string rdlcFilePath2 = args[3];

                Dictionary<string, string> parameters = new Dictionary<string, string>() {
                {"PARAMETER KEY",args[0]};
                Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
                Encoding.GetEncoding("windows-1252");
                LocalReport report = new LocalReport(rdlcFilePath2);
        
                report.AddDataSource("YOUR DATA SOURCE NAME", JsonConvert.DeserializeObject<List<YOUR_MODEL>>(File.ReadAllText(args[1])));
                    result = report.Execute(RenderType.Pdf, 1, parameters);
        
                using (var fs = new FileStream(args[2], FileMode.Create, FileAccess.Write))
                {
                    fs.Write(result.MainStream);
                }
            }, new string[] { "YOUR PARAMETER VALUE", jsonDataFilePath , generatedFilePath, rdlcFilePath });

            var memory = new MemoryStream();
            using (var stream = new FileStream(Path.Combine("", generatedFilePath), FileMode.Open))
            {
                stream.CopyTo(memory);
            }

            File.Delete(generatedFilePath);
            File.Delete(jsonDataFilePath );
            memory.Position = 0;
            return memory.ToArray();
        }

With the help of above code you can also include some conditions at the level of RDLC file path generation and achieve multiple RDLC execution.

But how to Pass Multiple Parameters to Report

@Dawood-Jamil
Copy link

@Akash-JS but how do you read response from it (e.g. ReportResult from LocalReport.Execute())

This is something which is already explained by @tharindubuddhi , though I am providing a WORKING EXAMPLE here.
Prerequisite - newtonsoft nuget, Tmds.ExecFunction nuget

private FunctionExecutor FunctionExecutor = new FunctionExecutor(
            o =>
            {
                o.StartInfo.RedirectStandardError = true;
                o.OnExit = p =>
                {
                    if (p.ExitCode != 0)
                    {
                        string message = $"Function exit code failed with exit code: {p.ExitCode}" + Environment.NewLine +
                                          p.StandardError.ReadToEnd();
                        throw new Exception(message);
                    }
                };
            }
            );

public byte[] GenerateReport()
        {
            string guID = Guid.NewGuid().ToString().Replace("-", "");
            string fileDirPath = Assembly.GetExecutingAssembly().Location.Replace("YOUR DLL NAME", string.Empty);
            string rdlcFilePath = string.Format("{0}RDLC\\{1}.rdlc", fileDirPath, "YOUR RDLC NAME");
            string generatedFilePath = string.Format("{0}\\{1}.pdf", fileDirPath, guID);
            string jsonDataFilePath = string.Format("{0}\\{1}.json", fileDirPath, guID);
            File.WriteAllText( jsonDataFilePath , JsonConvert.SerializeObject("YOUR SERVICE METHOD TO FETCH DATA"));

            FunctionExecutor.Run((string[] args) =>
            {
                // 0 : Your Report Parameter IF ANY
                // 1 : Data file path - jsonDataFilePath 
                // 2 : Filename - generatedFilePath 
                // 3 : RDLCPath - rdlcFilePath 
                ReportResult result;

                string rdlcFilePath2 = args[3];

                Dictionary<string, string> parameters = new Dictionary<string, string>() {
                {"PARAMETER KEY",args[0]};
                Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
                Encoding.GetEncoding("windows-1252");
                LocalReport report = new LocalReport(rdlcFilePath2);
        
                report.AddDataSource("YOUR DATA SOURCE NAME", JsonConvert.DeserializeObject<List<YOUR_MODEL>>(File.ReadAllText(args[1])));
                    result = report.Execute(RenderType.Pdf, 1, parameters);
        
                using (var fs = new FileStream(args[2], FileMode.Create, FileAccess.Write))
                {
                    fs.Write(result.MainStream);
                }
            }, new string[] { "YOUR PARAMETER VALUE", jsonDataFilePath , generatedFilePath, rdlcFilePath });

            var memory = new MemoryStream();
            using (var stream = new FileStream(Path.Combine("", generatedFilePath), FileMode.Open))
            {
                stream.CopyTo(memory);
            }

            File.Delete(generatedFilePath);
            File.Delete(jsonDataFilePath );
            memory.Position = 0;
            return memory.ToArray();
        }

With the help of above code you can also include some conditions at the level of RDLC file path generation and achieve multiple RDLC execution.

But how to Pass Multiple Parameters to Report

Here is my Code:
string Path = $"{_env.WebRootPath}\Reports\invoiceDine.rdlc";

DataTable dt = new DataTable();
dt = GetInvoiceItems(invoiceFromDb.Id);

LocalReport report = new LocalReport(Path);
report.AddDataSource("dsInvoiceDine", dt);

Dictionary<string, string> param = new Dictionary<string, string>();
param.Add("bParam", $"{invoiceFromDb.Id}");
param.Add("gParam", $"{salesOrderFromDb.Guests}");
param.Add("tParam", $"{invoiceFromDb.Table.Table}");
param.Add("dParam", $"{invoiceFromDb.Time}");
param.Add("totalP", $"{invoiceFromDb.SubTotal}");
param.Add("t1", $"{tax1}");
param.Add("t2", $"{tax2}");
param.Add("tA1", $"{tax1Amount}");
param.Add("tA2", $"{tax2Amount}");
param.Add("AT1", $"{totalAmountWithTax1}");
param.Add("AT2", $"{totalAmountWithTax2}");
param.Add("terminalParam", $"{terminalFromDb.Name}");
param.Add("addressParam", $"{t.Address}");
param.Add("serviceParam", "Service Charges of applicable on table of " + $"{personForServiceCharges}" + " & Above");

var result = report.Execute(RenderType.Pdf, 1, param);
return File(result.MainStream,"application/Pdf");

How i can Pass my parameters to FunctionExecutor.

@tawfiqdawod
Copy link

Hello,
I faced same problem, with reporting in asp core,
when I try to fork process using your code, there is an exception here:
using (var stream = new FileStream(Path.Combine("", generatedFilePath), FileMode.Open))
{
stream.CopyTo(memory);
}

pdf file not found exception,
the process in FunctionExecutor doesn't create a pdf file,
and I cannot debug because it is not in main thread.
please help

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

No branches or pull requests