早在 .NET 6 时期发布 MAUI 时就已经对 C# 非常感兴趣,加上这几年对游戏开发的学习使得我重新关注这款语言平台

关联阅读:

如今 .NET 8 新增了各种能力:例如进一步对 wasm 的支持(Blazor 美如画),还有 NativeAOT。再加上微软自从改变了路线开始尊重开源社区,并且先进技术跟得一次比一次快,让我又当场狗叫

「.NET is THE NEXT GENERATION」

总所周知 CloudFlare Workers 本来是一个服务托管平台,还是跟上一篇讲得差不多:它优先托管基于 NodeJS 的应用或者 FaaS。但是!它确实也进入了 WASI 的实验性支持

所以当我单方面狗叫「WebAssembly is FUTURE」之时,我已经在想:用 C# 狸猫换太子是不是有戏?

醉翁之意不在酒啊

在 .NET 8,WASI 创建相当丝滑

这个事情起源于 .NET 7,这个时候需要一个 SDK:SteveSandersonMS/dotnet-wasi-sdk: Packages for building .NET projects as standalone WASI-compliant modules

本质还是创建一个 ASP.NET 应用,但是将 ASP 关于网络部分重新以 WASI 实现

而 .NET 8 开始正经支持,并且提供了新的 workload(按照我们老微软正田字旗的说法叫:工作负载)

dotnet workload install wasi-experimental

接着,为了它的 Publish 或者是 AOT(?) 能正常实现,需要自行在环境中安装 wasi-sdk

Releases · WebAssembly/wasi-sdk

.NET 在编译的时候会自行去找本地的 wasi-sdk,但前提是这个终端环境中有 WASI_SDK_PATH 变量。所以还是记得先根据 wasi-sdk 的 Readme 中关于 InstallUse 部分先做好

然后就可以 Create 一个项目。当然如果使用的是 Jetbrain Rider 的话,新建项目列表已经将这个 workload 扫出来了

这个时候打开 csproj 文件搂一眼会发现,Runtime 不是 web 不是 console

而是 wasi-wasm

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFramework>net8.0</TargetFramework>
    <RuntimeIdentifier>wasi-wasm</RuntimeIdentifier>
    <OutputType>Exe</OutputType>
    <PublishTrimmed>true</PublishTrimmed>
  </PropertyGroup>
</Project>

这个时候如果什么都不做(反正也有个 hello world 能跑),直接编译,会按照经典 .NET 老配方运行 —— 代码将会编程一些中间代码(已经分门别类编译成 dll),最后让小 Runtime 就着 Wasm 环境来跑

显然我不要这个配方,我要的是单独一个 .wasm 文件以便 wasmedge 或者 wasmtime 直接执行起来,就像上一篇提到的一样

所以这个时候需要向 csproj 文件再杵一行进去

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFramework>net8.0</TargetFramework>
    <RuntimeIdentifier>wasi-wasm</RuntimeIdentifier>
    <OutputType>Exe</OutputType>
    <PublishTrimmed>true</PublishTrimmed>
+   <WasmSingleFileBundle>true</WasmSingleFileBundle>
  </PropertyGroup>
</Project>

再次编译就是单文件版本了

一般这个时候如果拿去 docker 运行,跑下来会大概 10MB 左右上上和下下,所以可以根据情况再对运行环境有些优化或者调整,例如

<EventSourceSupport>false</EventSourceSupport>
<UseSystemResourceKeys>true</UseSystemResourceKeys>
<EnableUnsafeUTF7Encoding>false</EnableUnsafeUTF7Encoding>
<HttpActivityPropagationSupport>false</HttpActivityPropagationSupport>
<DebuggerSupport>false</DebuggerSupport>

接着 wasm-opt 也可以用用(这在 rust 已经很常见,还是 wasm-pack 的内置优化项)

完事,根据 wrangler 的文档就可以把这个 wasm 文件送上去,过一小段时间之后就可以当成一个服务调用

关联阅读:

然而,.NET 8 的 WASI 转正,带来的却是暂时不直接支持 http 应用。即 system.net 并没有提供给 WASI 平台,要稳定支持且更多相关的功能还是得等到 .NET 9

或者还有一些路线

事实上,答案就在 rust version sdk 里

Cloudflare Workers 直接明牌写着:

Write in JS, Rust, C, and C++

又众所周知,CF Workers 实际上就是整了个 V8 在提供服务

由此可得,RustC++ 的支持指腚有问题。于是我们可以先去看一眼 tutorial 学习一手

关联阅读:

结果,不仅代码上有明显的 wasm_bindgen,而且 rust 代码中各种要调用环境外扩展标记,证明 rust 确实是在 WebAssembly 中运行

并且 rustwasm-worker-template 的 README 还清晰的写下了这个:

workers-rs (the Rust SDK for Cloudflare Workers used in this template) is meant to be executed as compiled WebAssembly, and as such so must all the code you write and depend upon.

所以,C# 完整的在 worker 上跑起来不是不可能的。并且有另外一个托管平台 spin 就实现了相应的 SDK

但在我印象中,.NET 社区的活跃程度应该是离谱的高的,只是玩 .NET 的家人们应该还不屑于这种玩法,以致还没有相应的 NuGet 吧