Rendering .rdlc reports with ASP .NET Core - asp.net-core

Is it possible to render .rdlc reports with ASP.NET Core? Currently this only seems to be possible if I target the .NET Framework as opposed to .NET Core.
I don't need a report viewer I just need to render the results of an .rdlc report as a byte array.

If you want to create pdf/excel/word using rdlc report I recommend you can use AspNetCore.Reporting library. This is open source and comes as a nuget package. you can integrate this in your .NET Core API or .NET Core Azure function. You can generate a byte array convert it to base 64 string and retrieve that to your client side. More on the link in the comment.

You very well can render rdlc into a byte array. Please see a related question I asked a while back. RDLC Local report viewer for ASP.NET Core and Angular(>2.0).
Eventually a creative discussion on that thread resulted in an angular package(https://www.npmjs.com/package/ng2-pdfjs-viewer - Disclosure; I am the author) with consumable rdlc byte array functionality on client side. Of course, instead of this package, you may choose another javascript library to display the byte array.
A simple usage on angular would be like this. Please note, most of the code can be reused even if you are using plain js or another framework.
The below code demonstrates
1. Spitting byte array using RDLC report viewer control on aspnet core action method(on server side) and sending it over wire using http. (Code is in C#)
2. Processing response's byte array into a blob object (Js)
3. Feeding blob object into ng2-pdfjs-viewer.
4. ng2-pdfjs-viewer internally uses Mozilla's PDFJS to accomplish the feat of displaying the PDF on browser.
(FYI.. I took code from samples provided on ng2-pdfjs-viewer package. Replace step 3 and 4 if you are using another library or plain javascript)
<!-- your.component.html -->
<button (click)="showPdf();">Show</button>
<div style="width: 800px; height: 400px">
<ng2-pdfjs-viewer #pdfViewer></ng2-pdfjs-viewer>
</div>
export class MyComponent implements OnInit {
#ViewChild('pdfViewer') pdfViewer
...
private downloadFile(url: string): any {
return this.http.get(url, { responseType: ResponseContentType.Blob }).map(
(res) => {
return new Blob([res.blob()], { type: "application/pdf" });
});
}
public showPdf() {
let url = "http://localhost/api/GetMyPdf";
this.downloadFile(url).subscribe(
(res) => {
this.pdfViewer.pdfSrc = res; // <---- pdfSrc can be Blob or Uint8Array
this.pdfViewer.refresh(); // Ask pdf viewer to load/reresh pdf
}
);
}
[HttpGet]
[Route("MyReport")]
public IActionResult GetReport()
{
var reportViewer = new ReportViewer {ProcessingMode = ProcessingMode.Local};
reportViewer.LocalReport.ReportPath = "Reports/MyReport.rdlc";
reportViewer.LocalReport.DataSources.Add(new ReportDataSource("NameOfDataSource1", reportObjectList1));
reportViewer.LocalReport.DataSources.Add(new ReportDataSource("NameOfDataSource2", reportObjectList1));
Warning[] warnings;
string[] streamids;
string mimeType;
string encoding;
string extension;
var bytes = reportViewer.LocalReport.Render("application/pdf", null, out mimeType, out encoding, out extension, out streamids, out warnings);
return File(bytes, "application/pdf")
}

In case anyone is still looking for a similar solution, I would recommend using "ReportViewerCore.NETCore".
Here is the nuGet reference - https://www.nuget.org/packages/ReportViewerCore.NETCore/
Here is the github link to the repo - https://github.com/lkosson/reportviewercore/
Basic usage
Stream reportDefinition; // your RDLC from file or resource
IEnumerable dataSource; // your datasource for the report
LocalReport report = new LocalReport();
report.LoadReportDefinition(reportDefinition);
report.DataSources.Add(new ReportDataSource("source", dataSource));
report.SetParameters(new[] { new ReportParameter("Parameter1", "Parameter value") });
byte[] pdf = report.Render("PDF");

You can not render .rdlc reports in .NET Core, using .rdlc as a byte array.

Rendering RDLC reports relies on WinForms and WebForms and is currently only supported on .NET Framework. Hopefully Microsoft will make a (slimmed down) version available (just rendering the reports for starters) on .NET Core or .NET 5, but no word as of yet.
As an alternative, you could go for a solution where you run report rendering as a separate ASP.NET 2 app targeting .NET Framework, while the rest of your app could target .NET Core 3 or later. That way, you can call the reporting endpoints with the appropriate data, and it returns a rendered report.
This is what I've done, creating a sort of microservices architecture where multiple .Net Core 3.1 apps can post both the XML RDLC report definition and data to an endpoint running on net48, which uses the LocalReport class to render the report to the desired format, returning it as a byte array.
Angular app | ----> | APIs (.Net Core 3.1) | ----> | API (.Net Framework 4.8)

Related

A best Html to PDF converter in ASP.NET Core 6.0

I want to convert html to pdf in my website (ASP.NET Core 6.0), but I can't find the solution.
It should be created by specifing a url.
In ASP.NET MVC 5 era, I had used the library called Tuespeckin.
Is there any solution, which is free and elegant , that can work in ASP.NET Core 6.0?
Although following article was so helpful, it may a little obsolete.
Can PDFSharp create Pdf file from a Html string in Net Core?
First answer mention SelectPDF, but I wish more modern methods, if any.
You can use Syncfusion to convert html to pdf.
First, Install Syncfusion.HtmlToPdfConverter.Net.Windows NuGet package.
Then, Edit the corresponding method in the controller:
public class HomeController : Controller
{
public IActionResult Index()
{
return View();
}
public IActionResult ExportToPDF()
{
HtmlToPdfConverter htmlConverter = new HtmlToPdfConverter();
//Convert URL to PDF document
PdfDocument document = htmlConverter.Convert("https://www.syncfusion.com");
//Create memory stream
MemoryStream stream = new MemoryStream();
//Save the document
document.Save(stream);
return File(stream.ToArray(), System.Net.Mime.MediaTypeNames.Application.Pdf, "HTML-to-PDF.pdf");
}
}
Don't forget to add the namespace:
using Syncfusion.Pdf;
using Syncfusion.HtmlConverter;
Finally, Add the call in Index.cshtml:
#{
Html.BeginForm("ExportToPDF", "Home", FormMethod.Post);
{
<div>
<input type="submit" value="Convert HTML to PDF" style="width:250px;height:27px" />
</div>
}
Html.EndForm();
}
You can change the URL Page you want to download in the controller method:
PdfDocument document = htmlConverter.Convert("Your Url");
For more details, you can refer to this document.
Update:
Yes, it's not actually open source, you need to get a license key to remove it (only 30 days free trial).
Free and effective first choice is actually SelectPDF.
If you really don't want to use it, you can choose the following two:
PuppeteerSharp:
It's a headless chrome instance which can capture pdfs, and has a nice C# library (Puppeteer Sharp).
jsPDF:
This is also a conversion tool used by open source.
Just my opinion, I think the experience of these two is not as good as SelectPDF. Maybe there are other free and up-to-date PDF converters, but I haven't found them and haven't used them.

JsonConvert DataSet .NET Core 2.0 not working

This example no longer seems to work in .NET Core 2.0, it now serializes using an XmlDiffGram. Any easy way to get it working? Do I need to do this whole thing?
As of 25/04/2018. Download latest version of Newtonsoft. I upgraded to 11.0.2. It now works with ASP Core 2. Datasets get converted to JSON.
It's looks like the Netonsoft crew have written a specific converters for DataSet & DataTables which should point you in the right direction.
Quick update:
It looks like these are not quite in the latest nuget release yet, but coming soon.
when they are released, you'll need to change the line in the example to something like.
string json = JsonConvert.SerializeObject(dataSet, Formatting.Indented, new JsonSerializerSettings { Converters = new[] { new Newtonsoft.Json.Converters.DataSetConverter() } });
Well this works...
var xml = new XDocument();
using (var writer = xml.CreateWriter())
{
dataSet.WriteXml(writer);
writer.Flush();
}
return Json(xml);

Migirating to Stimulsoft Core

For getting Pdf report in Asp.net MVC I am working with Stimulsoft 2015. The problem is that I have no idea how to convert my code in order to work with Stimulsoft Core in Asp.net Core. it seems some features are not available anymore in Stimulsoft Core (like StiReport).
This is the code which works fine in Asp.net MVC
public ActionResult GetReportSnapshot(string sort)
{
StiReport report = new StiReport();
report.Load(Server.MapPath("~/Reports/Jobs.mrt"));
report["#PrjectId"] = 1;
report["#OrderBy"] = sort;
report.Dictionary.Variables["title"] = new Stimulsoft.Report.Dictionary.StiVariable("title", sort);
report.Render();
MemoryStream stream = new MemoryStream();
report.ExportDocument(StiExportFormat.Pdf, stream);
stream.Position = 0;
FileStreamResult fsr = new FileStreamResult(stream, "application/pdf");
return fsr;
}
I will appreciate any help.
In NuGet Package Stimulsoft.Reports.Web.NetCore version 2018.3.5.
and Asp.Net core 2.0.
This is working for me, Try this:
public IActionResult GetReportSnapshot(string sort)
{
StiReport report = new StiReport();
report.Load(#"C:\Users\Admin\Desktop\report.mrt"); // laod report
report.Render();
report["#PrjectId"] = 1;
report["#OrderBy"] = sort;
report.Dictionary.Variables["title"] = new Stimulsoft.Report.Dictionary.StiVariable("title", sort);
// Create an PDF settings instance. You can change export settings.
var settings = new Stimulsoft.Report.Export.StiPdfExportSettings();
// Create an PDF service instance.
var service = new Stimulsoft.Report.Export.StiPdfExportService();
// Create a MemoryStream object.
var stream = new MemoryStream();
// Export PDF using MemoryStream.
service.ExportTo(report, stream, settings);
return File(stream.ToArray(), "application/octet-stream");
}
What nuget packages are you using? It could be you are missing the nuget packages containing the StiReport class. (I see they split up their library over multiple nuget packages)
Also it could be they have not migrated this part to dotnet core yet.
i'd advise you to click around there github repo and see if you can find any information there: https://github.com/stimulsoft, or on there website.
By the looks of nuget they have only recently started to migrate to dotnet core so this would suppose my second suggestion is the right suggestion.

.Net Core Image Manipulation (Crop & Resize) / File Handling

I have spent the last 5 hours trying to find a feasible way to accomplish what seems to me quite an easy task if it was a previous version of the .NET family that I was working with:
Uploading a picture
Resizing & Cropping the picture
Saving the new picture into a directory
I have come accross to couple of libraries that are either in pre-release stage or in a not-complete stage.
Has anyone at all accomplished the above tasks without specifically including the System.Drawing namespace and/or adding a dependency for an earlier version of the .NET framework?
UPDATE on 08 / 08 / 2016
I ended up using System.Drawing something which is very annoying and disappointing. If you are developing a software used by thousands of developers, and if all these developers are relying on the components of this software, I believe, one cannot just come up with a new version, "sweet talk" about it during the conferences to show off your public speaking skills rather than giving a shit about your work and on one hand proudly hold in high esteem of it, and on the other, strip away the mostly used and demanded portions of it.
I do understand and appreciate with great excitement myself - of the new era of .net with the core framework - being a loyal asp dev since the first days of classic asp - however, to me, it is just an uncomplete product causing more frustrations and dissappointment than pleasure. When there are millions of websites in today's content-driven world, completely relying on content management, you can't just come up and say, "Hey, I have this brilliant, techonology, leaner, faster blah blah" but errr, you will have some problems with "managing" your content..
It should not be forgotten that, although, MS (and us) is very excited about this new core framework, with now being open source etc, there are other languages and frameworks out there that are doing what MS is promising to do, for a very very long time now.
ImageSharp
ImageSharp is a new, fully featured, fully managed, cross-platform, 2D graphics API.
Designed to democratize image processing, ImageSharp brings you an incredibly powerful yet beautifully simple API.
Compared to System.Drawing we have been able to develop something much more flexible, easier to code against, and much, much less prone to memory leaks. Gone are system-wide process-locks; ImageSharp images are thread-safe and fully supported in web environments.
Built against .Net Standard 1.1 ImageSharp can be used in device, cloud, and embedded/IoT scenarios.
You can use the Microsoft ASP.NET Core JavaScript Services to invoke arbitrary NPM packages at runtime from .NET code which means you can choose any npm package that provide image scaling and invoke it.
The following example shows how to use JavaScriptServices to resize image
https://github.com/aspnet/JavaScriptServices/tree/dev/samples/misc/NodeServicesExamples
Hope that Helps
.NET Core Image Processing blog post (January 19, 2017) compares 6 libraries:
CoreCompat.System.Drawing
ImageSharp
Magick.NET (Win only)
SkiaSharp
FreeImage-dotnet-core
MagicScaler
Feb 26 update: post was updated, two new packages added
To complete the #Hossam Barakat answer, you can use the Microsoft ASP.NET Core JavaScript Services to invoke arbitrary NPM packages at runtime from .NET code which means you can choose any npm package that provide image scaling and invoke it.
The sample use the sharp module, which has a lot of dependecies. If you prefere, like me, to use jimp which is pure javascript:
Startup.cs
public class Startup
{
...
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
// Enable Node Services
services.AddNodeServices();
...
}
...
}
ImageController.cs
using System;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.NodeServices;
using Microsoft.AspNetCore.StaticFiles;
using System.Security.Cryptography;
using System.Text;
namespace NodeServicesExamples.Controllers
{
public class ResizeImageController : Controller
{
private const int MaxDimension = 1000;
private static string[] AllowedMimeTypes = new[] { "image/jpeg", "image/png", "image/gif" };
private IHostingEnvironment _environment;
private INodeServices _nodeServices;
public ResizeImageController(IHostingEnvironment environment, INodeServices nodeServices)
{
_environment = environment;
_nodeServices = nodeServices;
}
[Route("resize/{*imagePath}")]
[ResponseCache(Duration = 3600)]
public async Task<IActionResult> Index(string imagePath, double maxWidth, double maxHeight)
{
// Validate incoming params
if (maxWidth < 0 || maxHeight < 0 || maxWidth > MaxDimension || maxHeight > MaxDimension
|| (maxWidth + maxHeight) == 0)
{
return BadRequest("Invalid dimensions");
}
var mimeType = GetContentType(imagePath);
if (Array.IndexOf(AllowedMimeTypes, mimeType) < 0)
{
return BadRequest("Disallowed image format");
}
// Locate source image on disk
var fileInfo = _environment.WebRootFileProvider.GetFileInfo(imagePath);
if (!fileInfo.Exists)
{
return NotFound();
}
var eTag = GenerateETag(Encoding.UTF8.GetBytes($"{fileInfo.LastModified.ToString("s")}-{fileInfo.Length}"));
HttpContext.Response.Headers["ETag"] = eTag;
var match = HttpContext.Request.Headers["If-None-Match"].FirstOrDefault();
if (eTag == match)
{
return StatusCode(304);
}
// Invoke Node and pipe the result to the response
var imageStream = await _nodeServices.InvokeAsync<Stream>(
"./Node/resizeImage",
fileInfo.PhysicalPath,
mimeType,
maxWidth,
maxHeight);
return File(imageStream, mimeType, fileInfo.Name);
}
private string GetContentType(string path)
{
string result;
return new FileExtensionContentTypeProvider().TryGetContentType(path, out result) ? result : null;
}
private string GenerateETag(byte[] data)
{
string ret = string.Empty;
using (var md5 = MD5.Create())
{
var hash = md5.ComputeHash(data);
string hex = BitConverter.ToString(hash);
ret = hex.Replace("-", "");
}
return ret;
}
}
}
Node\resizeImage.js
var jimp = require("jimp");
module.exports = function (result, physicalPath, mimeType, maxWidth, maxHeight) {
// Invoke the 'jimp' NPM module, and have it pipe the resulting image data back to .NET
jimp.read(physicalPath).then(function (file) {
var width = maxWidth || jimp.AUTO;
var height = maxHeight || jimp.AUTO;
file.resize(maxWidth, height)
.getBuffer(mimeType, function (err, buffer) {
var stream = result.stream;
stream.write(buffer);
stream.end();
});
}).catch(function (err) {
console.error(err);
});
};
install jimp: npm install jimp --save
Short answer is no, not yet. Most if not all current libraries rely on System.Drawing. If you need this now, I would go that route and add System.Drawing.
The .NET team is currently working through the features that are missing on the Core 1.0 stack, but this one isn't high enough on their priority list: Link
This is a library to watch as they're getting very close to a releasable API without System.Drawing. : ImageSharp
use SkiSharp, it is on the official microsoft documentation also:
https://learn.microsoft.com/en-us/dotnet/api/skiasharp?view=skiasharp-1.68.0

How can I make RestSharp use BSON?

I'm using RestSharp, and using Json.NET for serialization (see here).
Json.NET supports BSON, and since some of my requests have huge blocks of binary data, I think this would speed up my application dramatically. However, as far as I can tell, RestSharp does not seem to have any in-built support for BSON.
The use of Json.NET is implemented as a custom serializer for RestSharp, and so at first glance it looks like it would be possible to modify that custom serializer to use BSON. But, the Serialize method of the RestSharp.Serializers.ISerializer interface returns a string - which is (I assume) unsuitable for BSON. So, I assume that it would take some more significant changes to RestSharp to implement this change.
Has anyone figured out a way to do this?
Update: I looked at the RestSharp source, and discovered that the RestRequest.AddBody method that takes my object and serializes it into the request body eventually calls Request.AddParameter (with the serialized object data, and the parameter type RequestBody).
I figured that I might be able to serialize my object to BSON and then call Request.AddParameter directly - and indeed I can. However, when RestSharp then executes the RestRequest, it fails to put the binary content into the request, because there are other embedded assumptions about the request content being UTF-8 encoded.
Thus it looks like this hack would not work - there would need to be some changes made to RestSharp itself, and I'm not the man for the job...
Update 2: I decided to have a go at using the debugger to figure out how much of RestSharp I'd have to change to overcome the body-encoding issue, so I swapped out my NuGet version of RestSharp and included the RestSharp project in my solution. And... it worked.
It turns out that there has been a change to RestSharp in the last few months that isn't yet in the NuGet version.
So, you can now use AddParameter and pass in an already-BSON-encoded object, and RestSharp will send it off to the server without complaint.
Per the updates in my question, it turns out that if you have the latest RestSharp source, then instead of this:
request.AddBody(myObject);
... you can do this instead whenever you have a payload that would benefit from using BSON:
using (var memoryStream = new System.IO.MemoryStream())
{
using (var bsonWriter = new Newtonsoft.Json.Bson.BsonWriter(memoryStream))
{
var serializer = new Newtonsoft.Json.JsonSerializer();
serializer.Serialize(bsonWriter, myObject);
var bytes = memoryStream.ToArray();
request.AddParameter("application/bson", bytes, RestSharp.ParameterType.RequestBody);
}
}
Note that the first parameter to AddParameter is supposedly the parameter name, but in the case of ParameterType.RequestBody it's actually used as the content type. (Yuk).
Note that this relies on a change made to RestSharp on April 11 2013 by ewanmellor/ayoung, and this change is not in the current version on NuGet (104.1). Hence this will only work if you include the current RestSharp source in your project.
Gary's answer to his own question was incredibly useful for serializing restful calls. I wanted to answer how to deserialize restful calls using JSON.NET. I am using RestSharp version 104.4 for Silverlight. My server is using Web API 2.1 with BSON support turned on.
To accept a BSON response, create a BSON Deserializer for RestSharp like so
public class BsonDeserializer : IDeserializer
{
public string RootElement { get; set; }
public string Namespace { get; set; }
public string DateFormat { get; set; }
public T Deserialize<T>(IRestResponse response)
{
using (var memoryStream = new MemoryStream(response.RawBytes))
{
using (var bsonReader = new BsonReader(memoryStream))
{
var serializer = new JsonSerializer();
return serializer.Deserialize<T>(bsonReader);
}
}
}
}
Then, ensure your request accepts "application/bson"
var request = new RestRequest(apiUrl, verb);
request.AddHeader("Accept", "application/bson");
And add a handler for that media type
var client = new RestClient(url);
client.AddHandler("application/bson", new BsonDeserializer());