翻译|使用教程|编辑:龚雪|2024-04-29 10:30:46.777|阅读 16 次
概述:本文将为大家介绍如何通过.NET和WebAssembly集成如何在Node.js应用程序中创建报表,欢迎下载相关组件体验!
# 慧都年终大促·界面/图表报表/文档/IDE等千款热门软控件火热促销中 >>
DevExpress Reporting是.NET Framework下功能完善的报表平台,它附带了易于使用的Visual Studio报表设计器和丰富的报表控件集,包括数据透视表、图表,因此您可以构建无与伦比、信息清晰的报表。
DevExpress技术交流群10:532598169 欢迎一起进群讨论
自定义DevExpress报表(在后端应用程序中)可能会给刚接触 .NET的前端Web开发人员带来独特的挑战,在本文中我们将向您展示.NET和WebAssembly集成是如何帮助解决这些挑战,并增强整体应用程序的安全性和性能的。
传统的报表开发/定制方法需要 .NET和相关语言的专业知识,但WebAssembly消除了这个困难。通过在WASM环境中运行.NET应用程序,开发人员无需深入了解.NET就可以将交互式报表集成到Node.js应用程序中。
这种集成的第二个好处与安全性有关,WebAssembly在隔离的环境中执行代码。因此开发人员可以完全控制磁盘、网络和其他关键资源,这种隔离的执行环境可以降低与未授权访问相关的风险。
Microsoft在最近的.NET更新中一直致力于 .NET 和WebAssembly的集成,在.NET 7中,Micrsooft引入了CLI模板(如wasmconsole和wasmbrowser),并允许开发人员创建在承载.NET运行时的沙盒WebAssembly环境中运行的控制台和web应用程序。
随着.NET 8的发布,应用程序代码在编译时直接转换为WebAssembly,这一变化带来了与性能相关的显著改进,其特点是延迟减少、用户界面响应更快。
如果您是一个刚接触.NET的前端Web开发人员,并且对这篇内容感兴趣,建议创建一个应用程序,允许创建DevExpress报表并将其导出为PDF文件。
>dotnet new install Microsoft.NET.Runtime.WebAssembly.Templates::8.0.3
>dotnet workload install wasm-tools
运行以下命令创建一个示例wasm-export应用:
>dotnet new wasmconsole -o wasm-exporter
当示例项目准备好后,导航到项目文件夹:
>cd wasm-exporter
Program.cs文件包含以下代码:
using System; using System.Runtime.InteropServices.JavaScript; Console.WriteLine("Hello, Console!"); return 0; public partial class MyClass { [JSExport] internal static string Greeting() { var text = $"Hello, World! Greetings from node version: {GetNodeVersion()}"; return text; } [JSImport("node.process.version", "main.mjs")] internal static partial string GetNodeVersion(); }
如您所见,JavaScript导入Greeting .NET函数,而.NET本身导入一个函数,该函数显示当前安装的Node.js版本。
反过来,代码在main.mjs文件加载.NET运行时并将JavaScript函数导入WebAssembly:
import { dotnet } from './_framework/dotnet.js' const { setModuleImports, getAssemblyExports, getConfig } = await dotnet .withDiagnosticTracing(false) .create(); setModuleImports('main.mjs', { node: { process: { version: () => globalThis.process.version } } }); const config = getConfig(); const exports = await getAssemblyExports(config.mainAssemblyName); const text = exports.MyClass.Greeting(); console.log(text); await dotnet.run();
一旦您使用了dotnet build命令构建了这个应用程序,运行它的方式与您通常运行node.js应用程序的方式相同,来查看两个函数的执行结果:
>dotnet build
>node main.mjs
Hello, World! Greetings from node version: v18.12.1
Hello, Console!
对于未安装DevExpress的macOS/Linux或Windows操作系统,请执行以下操作:
dotnet new nugetconfig
完成后,安装在WebAssembly应用程序中创建文档所需的NuGet包:
dotnet add package Newtonsoft.Json dotnet add package DevExpress.Drawing.Skia --version 23.2.*-* dotnet add package DevExpress.Reporting.Core --version 23.2.*-* dotnet add package SkiaSharp.HarfBuzz --version 2.88.7 dotnet add package SkiaSharp.NativeAssets.WebAssembly --version 2.88.7 dotnet add package HarfBuzzSharp.NativeAssets.WebAssembly --version 2.8.2.4 dotnet add package SkiaSharp.Views.Blazor --version 2.88.7
在项目配置文件(wasm- exporters .csproj)中添加一个本地SkiaSharp依赖项:
<ItemGroup> <NativeFileReference Include="$(HarfBuzzSharpStaticLibraryPath)\2.0.23\*.a" /> </ItemGroup>
指定生成的可执行文件和库的路径:
<wasmappdir>./result</wasmappdir>
在这一点上,我们完成了准备工作,并准备开始编码。
我们的应用程序由两个部分组成:一个基于.NET服务器的应用程序编译成程序集,一个JavaScript客户端应用程序创建并配置.NET运行时环境,以便在WASM中运行该程序集。
在您喜欢的代码编辑器中打开文件夹,在Program.cs代码单元中实现以下类:ReportExporter、JsonSourceCustomizationService和SimplifiedUriJsonSource:
using System; using System.Collections.Generic; using System.ComponentModel.Design; using System.IO; using System.Reflection; using System.Runtime.InteropServices.JavaScript; using System.Threading; using System.Threading.Tasks; using DevExpress.Data; using DevExpress.DataAccess.Json; using DevExpress.XtraPrinting; using DevExpress.XtraReports.UI; return 0; public partial class ReportExporter { [JSExport] internal static async Task ExportToPdfAsync(JSObject exportModel, JSObject result) { using var report = new XtraReport(); ((IServiceContainer)report).AddService(typeof(IJsonSourceCustomizationService), new JsonSourceCustomizationService()); using var reportStream = new MemoryStream(exportModel.GetPropertyAsByteArray("reportXml")); report.LoadLayoutFromXml(reportStream, true); PdfExportOptions pdfOptions = report.ExportOptions.Pdf; if(exportModel.HasProperty("exportOptions")) { SimplifiedFillExportOptions(pdfOptions, exportModel.GetPropertyAsJSObject("exportOptions")); } using var resultStream = new MemoryStream(); await report.ExportToPdfAsync(resultStream, pdfOptions); result.SetProperty("pdf", resultStream.ToArray()); resultStream.Close(); } static void SimplifiedFillExportOptions(object exportOptions, JSObject jsExportOptions) { PropertyInfo[] propInfos = exportOptions.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance); foreach(PropertyInfo pi in propInfos) { if(!jsExportOptions.HasProperty(pi.Name)) continue; if(pi.PropertyType == typeof(bool)) { pi.SetValue(exportOptions, jsExportOptions.GetPropertyAsBoolean(pi.Name)); } else if(pi.PropertyType == typeof(string)) { pi.SetValue(exportOptions, jsExportOptions.GetPropertyAsString(pi.Name)); } else if(pi.PropertyType.IsEnum) { string val = jsExportOptions.GetPropertyAsString(pi.Name); if(Enum.IsDefined(pi.PropertyType, val)) { pi.SetValue(exportOptions, Enum.Parse(pi.PropertyType, val)); } } else if(pi.PropertyType.IsClass) { SimplifiedFillExportOptions(pi.GetValue(exportOptions), jsExportOptions.GetPropertyAsJSObject(pi.Name)); } } } } public class JsonSourceCustomizationService : IJsonSourceCustomizationService { public JsonSourceBase CustomizeJsonSource(JsonDataSource jsonDataSource) { return jsonDataSource.JsonSource is UriJsonSource uriJsonSource ? new SimplifiedUriJsonSource(uriJsonSource.Uri) : jsonDataSource.JsonSource; } } public partial class SimplifiedUriJsonSource : UriJsonSource { public SimplifiedUriJsonSource(Uri uri) : base(uri) { } public override Task GetJsonStringAsync(IEnumerable sourceParameters, CancellationToken cancellationToken) { return GetJsonData(Uri.ToString()); } [JSImport("getJsonData", "main.mjs")] internal static partial Task GetJsonData(string url); }
ReportExporter
该类实现ExportToPdfAsync方法并将其导出到JavaScript模块,该方法创建一个XtraReport实例,将JsonSourceCustomizationService自定义服务添加到报表对象模型,并将可选的导出选项从javascript对象映射到本地.NET对象,使用方法将报表导出为PDF。
JsonSourceCustomizationService
这个服务取代了值,使用满足Blazor限制的自定义对象。这是因为WebAssembly不允许HTTP请求,而报表模型可能会引用带有URI的JSON源。
SimplifiedUriJsonSource
该类是UriJsonSource类的后代,并将HTTP请求重定向到应用程序的javascript段。
main.mjs文件是应用程序的核心JS段,将其内容替换为以下代码:
// Import necessary modules. import { dotnet } from '._framework/dotnet.js'; import fs from 'fs'; import { get as httpGet } from 'http'; import { get as httpsGet } from 'https'; import url from 'url' // Configure .NET runtime for WASM execution. const { setModuleImports, getAssemblyExports, getConfig } = await dotnet .withDiagnosticTracing(false) .create(); setModuleImports('main.mjs', { getJsonData }); // Retrieve the exported methods from the WASM part. const config = getConfig(); const exports = await getAssemblyExports(config.mainAssemblyName); // Prepare the report model and related options. const repx = fs.readFileSync('../reports/report1.repx'); const model = { reportXml: repx, exportOptions: { DocumentOptions: { Application: "WASM", Subject: "wasm integration" }, PdfUACompatibility: "PdfUA1" } } // Export the report to PDF. const result = {}; await exports.ReportExporter.ExportToPdfAsync(model, result); const buffer = Buffer.from(result.pdf); fs.writeFileSync('result.pdf', buffer); // Define a method to fetch JSON data from a given URL. function getJsonData(jsonUrl) { return new Promise((resolve) => { const fetchMethod = url.parse(jsonUrl).protocol === 'https:' ? httpsGet : httpGet; fetchMethod(jsonUrl, res => { let data = ''; res.on('data', chunk => data += chunk); res.on('end', () => resolve(data)); }).on('error', err => resolve('')); }); } // Initiate the .NET runtime. await dotnet.run();
该文件中的代码:
要运行应用程序,首先构建.NET应用程序,导航到result文件夹,然后运行JavaScript应用程序:
>dotnet build
>cd result
>node main.mjs
应用程序在结果目录中创建一个新的result.pdf文件。
按照readme文件中列出的步骤,运行后端和客户端应用程序,并将浏览器指向客户端应用程序中指定的URL。结果将显示如下:
本站文章除注明转载外,均为本站原创或翻译。欢迎任何形式的转载,但请务必注明出处、不得修改原文相关链接,如果存在内容上的异议请邮件反馈至chenjj@cahobeh.cn
文章转载自:慧都网