翻譯|使用教程|編輯:龔雪|2024-04-29 10:30:46.777|閱讀 117 次
概述:本文將為大家介紹如何通過(guò).NET和WebAssembly集成如何在Node.js應(yīng)用程序中創(chuàng)建報(bào)表,歡迎下載相關(guān)組件體驗(yàn)!
# 界面/圖表報(bào)表/文檔/IDE等千款熱門(mén)軟控件火熱銷(xiāo)售中 >>
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開(kāi)發(fā)人員帶來(lái)獨(dú)特的挑戰(zhàn),在本文中我們將向您展示.NET和WebAssembly集成是如何幫助解決這些挑戰(zhàn),并增強(qiáng)整體應(yīng)用程序的安全性和性能的。
傳統(tǒng)的報(bào)表開(kāi)發(fā)/定制方法需要 .NET和相關(guān)語(yǔ)言的專(zhuān)業(yè)知識(shí),但WebAssembly消除了這個(gè)困難。通過(guò)在WASM環(huán)境中運(yùn)行.NET應(yīng)用程序,開(kāi)發(fā)人員無(wú)需深入了解.NET就可以將交互式報(bào)表集成到Node.js應(yīng)用程序中。
這種集成的第二個(gè)好處與安全性有關(guān),WebAssembly在隔離的環(huán)境中執(zhí)行代碼。因此開(kāi)發(fā)人員可以完全控制磁盤(pán)、網(wǎng)絡(luò)和其他關(guān)鍵資源,這種隔離的執(zhí)行環(huán)境可以降低與未授權(quán)訪(fǎng)問(wèn)相關(guān)的風(fēng)險(xiǎn)。
Microsoft在最近的.NET更新中一直致力于 .NET 和WebAssembly的集成,在.NET 7中,Micrsooft引入了CLI模板(如wasmconsole和wasmbrowser),并允許開(kāi)發(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)是延遲減少、用戶(hù)界面響應(yīng)更快。
如果您是一個(gè)剛接觸.NET的前端Web開(kāi)發(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();
}
如您所見(jiàn),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依賴(lài)項(xiàng):
<ItemGroup> <NativeFileReference Include="$(HarfBuzzSharpStaticLibraryPath)\2.0.23\*.a" /> </ItemGroup>
指定生成的可執(zhí)行文件和庫(kù)的路徑:
<wasmappdir>./result</wasmappdir>
在這一點(diǎn)上,我們完成了準(zhǔn)備工作,并準(zhǔn)備開(kāi)始編碼。
我們的應(yīng)用程序由兩個(gè)部分組成:一個(gè)基于.NET服務(wù)器的應(yīng)用程序編譯成程序集,一個(gè)JavaScript客戶(hù)端應(yīng)用程序創(chuàng)建并配置.NET運(yùn)行時(shí)環(huán)境,以便在WASM中運(yùn)行該程序集。
在您喜歡的代碼編輯器中打開(kāi)文件夾,在Program.cs代碼單元中實(shí)現(xiàn)以下類(lèi):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
該類(lèi)實(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ù)取代了值,使用滿(mǎn)足Blazor限制的自定義對(duì)象。這是因?yàn)閃ebAssembly不允許HTTP請(qǐng)求,而報(bào)表模型可能會(huì)引用帶有URI的JSON源。
SimplifiedUriJsonSource
該類(lèi)是UriJsonSource類(lèi)的后代,并將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)行后端和客戶(hù)端應(yīng)用程序,并將瀏覽器指向客戶(hù)端應(yīng)用程序中指定的URL。結(jié)果將顯示如下:
 
本站文章除注明轉(zhuǎn)載外,均為本站原創(chuàng)或翻譯。歡迎任何形式的轉(zhuǎn)載,但請(qǐng)務(wù)必注明出處、不得修改原文相關(guān)鏈接,如果存在內(nèi)容上的異議請(qǐng)郵件反饋至chenjj@ke049m.cn
文章轉(zhuǎn)載自:慧都網(wǎng)