如何在React浏览器端应用中无需服务器执行动态C#代码(含逻辑/公式)
如何在React浏览器端应用中无需服务器执行动态C#代码(含逻辑/公式)
根据你的场景——手握5000+现成的C#公式,要在React浏览器环境里动态执行、完全不依赖后端、尽量少改现有代码——这件事完全可以实现,核心思路是借助.NET的WebAssembly(WASM)运行时,搭配Roslyn的浏览器端编译能力,下面给你拆解几个最实用的方案:
方案1:Blazor WASM + Roslyn WASM 动态编译执行(最贴合你的需求)
这个方案几乎能1:1复用你现在桌面端的逻辑,不用手动改写公式,直接把动态编译执行的流程搬到浏览器里。
具体怎么做:
- 引入.NET WASM运行时和Roslyn WASM包:不用搭建完整的Blazor应用,只需要把必要的.NET WASM runtime和
Microsoft.CodeAnalysis.CSharp.Scripting.Wasm包引入到你的React项目中——现在微软已经把Roslyn编译能力打包成了可在浏览器运行的WASM模块。 - 移植现有公式处理逻辑:你之前解析公式片段、提取变量、生成完整C#类/方法的代码,都是C#写的对吧?直接把这部分代码放到WASM的.NET项目里就行,完全不用改逻辑,因为WASM里的.NET环境和桌面端几乎一致。
- React与WASM的互操作:当React的输入组件触发
onChange时,把参数值(比如日期、文本框内容)传给WASM里的.NET方法,调用动态编译后的EvaluateCode方法,拿到结果后再更新React的UI状态。
举个简化的例子:
在WASM端的C#代码:
using Microsoft.CodeAnalysis.CSharp.Scripting; using Microsoft.CodeAnalysis.Scripting; using System.Collections.Generic; public static async Task<object> RunFormula(string formulaSnippet, Dictionary<string, object> paramValues) { // 这里直接复用你之前生成完整类代码的逻辑 string fullClassCode = GenerateFullClassFromSnippet(formulaSnippet, paramValues.Keys); // 配置Roslyn脚本选项,添加必要的引用和命名空间 var scriptOpts = ScriptOptions.Default .AddReferences(typeof(DateTime).Assembly) .AddImports("System", "System.Collections.Generic"); // 编译并执行代码,传入参数 var result = await CSharpScript.EvaluateAsync(fullClassCode, scriptOpts, paramValues); return result; }
在React里调用:
// 假设已经加载了.NET WASM runtime async function handleInputUpdate(formulaId, params) { // 从你的存储中获取对应的C#公式片段 const formulaSnippet = getFormulaById(formulaId); // 调用WASM里的方法 const output = await window.dotnet.invokeMethodAsync('YourWasmAssembly', 'RunFormula', formulaSnippet, params); // 更新React状态 setFormResult(output); }
优缺点:
- ✅ 优点:完全复用现有C#公式和处理逻辑,不用手动改写5000个公式;支持动态编译任意公式;纯客户端执行,无需后端。
- ❌ 缺点:Roslyn WASM包体积不小,会增加前端打包大小;第一次加载时可能有短暂延迟;动态编译的性能比预编译稍差,但按需编译的话完全能满足输入变更的实时性需求。
方案2:提前批量编译公式为WASM模块(性能优先)
如果你的公式执行频率极高,动态编译的性能不能满足需求,可以提前把所有5000个公式编译成WASM模块,在React里按需加载调用。
具体怎么做:
- 写自动化编译工具:用Roslyn遍历所有公式,自动生成每个公式对应的C#类和方法,编译成.NET程序集。
- 编译为WASM模块:用
dotnet publish命令把这些程序集编译成可在浏览器运行的WASM模块。 - React中加载调用:通过.NET WASM runtime加载对应的模块,调用指定的公式方法,传递参数并获取结果。
优缺点:
- ✅ 优点:执行性能远高于动态编译,加载后执行速度快。
- ❌ 缺点:需要提前编译所有公式,新增或修改公式都要重新编译;灵活性不如动态编译,无法支持实时修改公式的场景。
方案3:Mono WASM Runtime(轻量替代)
如果觉得Blazor+Roslyn的体积太大,可以用Mono的WASM runtime,它更轻量,支持加载预编译的.NET程序集,也能实现简单的动态编译(不过Roslyn的支持不如官方完善)。
核心思路:
把你的公式处理逻辑编译成.NET程序集,用Mono WASM runtime加载,然后通过JS与WASM的互操作调用方法,传递参数。这个方案适合对包体积敏感,但动态编译需求不高的场景。
关键注意事项:
- 体积优化:不管用哪个方案,都要开启.NET的裁剪功能(
dotnet publish -c Release时默认启用),只保留必要的程序集,减少WASM包的大小。 - 互操作性能:JS和WASM之间的参数传递尽量简单,避免频繁传递大对象,提升交互流畅度。
- 安全问题:动态执行C#代码存在安全风险,但你的场景是使用自己的公式,所以无需担心;如果后续要支持用户自定义公式,需要做好沙箱隔离。
总结
最适合你的是方案1,它能最大化复用你现有的代码资产,不用改写5000个公式,完全满足客户端动态执行的需求。你只需要把之前CodeDom的编译逻辑替换成Roslyn的动态编译代码,再和React做简单的互操作即可。
内容来源于stack exchange




