翻譯|使用教程|編輯:龔雪|2024-04-29 10:30:46.777|閱讀 108 次
概述:本文將為大家介紹如何通過(guò).NET和WebAssembly集成如何在Node.js應(yīng)用程序中創(chuàng)建報(bào)表,歡迎下載相關(guān)組件體驗(yàn)!
# 界面/圖表報(bào)表/文檔/IDE等千款熱門軟控件火熱銷售中 >>
DevExpress Reporting是.NET Framework下功能完善的報(bào)表平臺(tái),它附帶了易于使用的Visual Studio報(bào)表設(shè)計(jì)器和豐富的報(bào)表控件集,包括數(shù)據(jù)透視表、圖表,因此您可以構(gòu)建無(wú)與倫比、信息清晰的報(bào)表。
DevExpress技術(shù)交流群10:532598169 歡迎一起進(jìn)群討論
自定義DevExpress報(bào)表(在后端應(yīng)用程序中)可能會(huì)給剛接觸 .NET的前端Web開發(fā)人員帶來(lái)獨(dú)特的挑戰(zhàn),在本文中我們將向您展示.NET和WebAssembly集成是如何幫助解決這些挑戰(zhàn),并增強(qiáng)整體應(yīng)用程序的安全性和性能的。
傳統(tǒng)的報(bào)表開發(fā)/定制方法需要 .NET和相關(guān)語(yǔ)言的專業(yè)知識(shí),但WebAssembly消除了這個(gè)困難。通過(guò)在WASM環(huán)境中運(yùn)行.NET應(yīng)用程序,開發(fā)人員無(wú)需深入了解.NET就可以將交互式報(bào)表集成到Node.js應(yīng)用程序中。
這種集成的第二個(gè)好處與安全性有關(guān),WebAssembly在隔離的環(huán)境中執(zhí)行代碼。因此開發(fā)人員可以完全控制磁盤、網(wǎng)絡(luò)和其他關(guān)鍵資源,這種隔離的執(zhí)行環(huán)境可以降低與未授權(quán)訪問(wèn)相關(guān)的風(fēng)險(xiǎn)。
Microsoft在最近的.NET更新中一直致力于 .NET 和WebAssembly的集成,在.NET 7中,Micrsooft引入了CLI模板(如wasmconsole和wasmbrowser),并允許開發(fā)人員創(chuàng)建在承載.NET運(yùn)行時(shí)的沙盒WebAssembly環(huán)境中運(yùn)行的控制臺(tái)和web應(yīng)用程序。
隨著.NET 8的發(fā)布,應(yīng)用程序代碼在編譯時(shí)直接轉(zhuǎn)換為WebAssembly,這一變化帶來(lái)了與性能相關(guān)的顯著改進(jìn),其特點(diǎn)是延遲減少、用戶界面響應(yīng)更快。
如果您是一個(gè)剛接觸.NET的前端Web開發(fā)人員,并且對(duì)這篇內(nèi)容感興趣,建議創(chuàng)建一個(gè)應(yīng)用程序,允許創(chuàng)建DevExpress報(bào)表并將其導(dǎo)出為PDF文件。
>dotnet new install Microsoft.NET.Runtime.WebAssembly.Templates::8.0.3
>dotnet workload install wasm-tools
運(yùn)行以下命令創(chuàng)建一個(gè)示例wasm-export應(yīng)用:
>dotnet new wasmconsole -o wasm-exporter
當(dāng)示例項(xiàng)目準(zhǔn)備好后,導(dǎo)航到項(xiàng)目文件夾:
>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導(dǎo)入Greeting .NET函數(shù),而.NET本身導(dǎo)入一個(gè)函數(shù),該函數(shù)顯示當(dāng)前安裝的Node.js版本。
反過(guò)來(lái),代碼在main.mjs文件加載.NET運(yùn)行時(shí)并將JavaScript函數(shù)導(dǎo)入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命令構(gòu)建了這個(gè)應(yīng)用程序,運(yùn)行它的方式與您通常運(yùn)行node.js應(yīng)用程序的方式相同,來(lái)查看兩個(gè)函數(shù)的執(zhí)行結(jié)果:
>dotnet build
>node main.mjs
Hello, World! Greetings from node version: v18.12.1
Hello, Console!
對(duì)于未安裝DevExpress的macOS/Linux或Windows操作系統(tǒng),請(qǐng)執(zhí)行以下操作:
dotnet new nugetconfig
完成后,安裝在WebAssembly應(yīng)用程序中創(chuàng)建文檔所需的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
在項(xiàng)目配置文件(wasm- exporters .csproj)中添加一個(gè)本地SkiaSharp依賴項(xiàng):
<ItemGroup> <NativeFileReference Include="$(HarfBuzzSharpStaticLibraryPath)\2.0.23\*.a" /> </ItemGroup>
指定生成的可執(zhí)行文件和庫(kù)的路徑:
<wasmappdir>./result</wasmappdir>
在這一點(diǎn)上,我們完成了準(zhǔn)備工作,并準(zhǔn)備開始編碼。
我們的應(yīng)用程序由兩個(gè)部分組成:一個(gè)基于.NET服務(wù)器的應(yīng)用程序編譯成程序集,一個(gè)JavaScript客戶端應(yīng)用程序創(chuàng)建并配置.NET運(yùn)行時(shí)環(huán)境,以便在WASM中運(yùn)行該程序集。
在您喜歡的代碼編輯器中打開文件夾,在Program.cs代碼單元中實(shí)現(xiàn)以下類: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
該類實(shí)現(xiàn)ExportToPdfAsync方法并將其導(dǎo)出到JavaScript模塊,該方法創(chuàng)建一個(gè)XtraReport實(shí)例,將JsonSourceCustomizationService自定義服務(wù)添加到報(bào)表對(duì)象模型,并將可選的導(dǎo)出選項(xiàng)從javascript對(duì)象映射到本地.NET對(duì)象,使用方法將報(bào)表導(dǎo)出為PDF。
JsonSourceCustomizationService
這個(gè)服務(wù)取代了值,使用滿足Blazor限制的自定義對(duì)象。這是因?yàn)閃ebAssembly不允許HTTP請(qǐng)求,而報(bào)表模型可能會(huì)引用帶有URI的JSON源。
SimplifiedUriJsonSource
該類是UriJsonSource類的后代,并將HTTP請(qǐng)求重定向到應(yīng)用程序的javascript段。
main.mjs文件是應(yīng)用程序的核心JS段,將其內(nèi)容替換為以下代碼:
// 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();
該文件中的代碼:
要運(yùn)行應(yīng)用程序,首先構(gòu)建.NET應(yīng)用程序,導(dǎo)航到result文件夾,然后運(yùn)行JavaScript應(yīng)用程序:
>dotnet build
>cd result
>node main.mjs
應(yīng)用程序在結(jié)果目錄中創(chuàng)建一個(gè)新的result.pdf文件。
按照readme文件中列出的步驟,運(yùn)行后端和客戶端應(yīng)用程序,并將瀏覽器指向客戶端應(yīng)用程序中指定的URL。結(jié)果將顯示如下:
本站文章除注明轉(zhuǎn)載外,均為本站原創(chuàng)或翻譯。歡迎任何形式的轉(zhuǎn)載,但請(qǐng)務(wù)必注明出處、不得修改原文相關(guān)鏈接,如果存在內(nèi)容上的異議請(qǐng)郵件反饋至chenjj@fc6vip.cn
文章轉(zhuǎn)載自:慧都網(wǎng)