Skip to content

Latest commit

 

History

History
181 lines (117 loc) · 11.6 KB

File metadata and controls

181 lines (117 loc) · 11.6 KB

[Proposal] Client-Side ASP.NET MVC Runtime for WebAssembly

Summary

This proposal introduces a client-side ASP.NET MVC runtime that executes Controllers, Razor Views, Routing, EF Core (SQLite), SignalR, and Identity entirely inside a WebAssembly Web Worker — with zero server dependency at runtime.

Developers write standard MVC code — Controllers, .cshtml Views, ViewData/ViewBag/TempData, Tag Helpers, Areas — and the SDK compiles it into static files deployable to any CDN, edge network, or embedded WebView.

A working implementation exists today: NetWasmMvc.SDK + Cepha.CLI on NuGet — source at sbay-dev/NetWasmMVCRuntime


The Problem

ASP.NET MVC is the most widely adopted .NET web programming model — yet it has no client-side story.

  • MVC requires a server. Developers with large MVC codebases have no path to deploy as static SPAs or to edge runtimes without a full rewrite.
  • No offline-first MVC. Server-rendered MVC cannot function without connectivity. There is no PWA-capable MVC with local Controllers, Views, and database access.
  • No "write once, deploy anywhere." You must choose between server-rendered MVC (requires infrastructure) or Blazor (different programming model). No option to run the same code as a browser SPA, a Node.js API, or an edge Worker.
  • Competitive gap. Next.js, Nuxt, and SvelteKit all support static-site generation and edge deployment. .NET MVC has no equivalent.

Proposed Solution

A new SDK target that compiles ASP.NET MVC for execution inside a WebAssembly Web Worker:

<Project Sdk="Microsoft.NET.Sdk.WebAssembly.Mvc">
  <PropertyGroup>
    <TargetFramework>net11.0</TargetFramework>
  </PropertyGroup>
</Project>

The developer writes standard MVC code — no new paradigm to learn:

public class HomeController : Controller
{
    public IActionResult Index()
    {
        ViewData["Title"] = "Welcome";
        return View();
    }

    [HttpPost]
    public IActionResult Contact(ContactForm form)
    {
        if (!ModelState.IsValid) return View(form);
        _db.Messages.Add(new Message { Name = form.Name, Body = form.Body });
        _db.SaveChanges();
        TempData["Success"] = "Message saved locally.";
        return RedirectToAction("Index");
    }
}

dotnet publish produces static files. No ASP.NET server required at runtime.

How it works: The .NET runtime boots inside a Web Worker, keeping the UI thread free. An MVC engine handles controller discovery, route matching, and action invocation. A pattern-matching Razor engine renders .cshtml views from embedded resources. The rendered HTML is sent to the main thread as frames applied via requestAnimationFrame. SQLite databases and session state persist to the Origin Private File System (OPFS). All C#↔JS communication uses JSImport/JSExport — no dependency on Blazor's infrastructure.

What's included

  • Full MVC pipelineController base class, ViewData/ViewBag/TempData/ModelState, ViewResult/PartialViewResult/JsonResult/RedirectResult, attribute routing, [Area], [ApiController], convention-based routing.
  • Razor rendering (no Roslyn)@Model, @ViewData, @if/@foreach, @RenderBody(), @RenderSection(), @Html.PartialAsync(), asp-controller/asp-action tag helpers.
  • EF Core + SQLite in WASM — Full LINQ, migrations, relationships. Database persisted to OPFS.
  • Client-side Identity — PBKDF2-SHA256 hashing, HMAC-signed session tokens, fingerprint binding, account lockout, cross-tab auth sync.
  • In-process SignalR — Hub discovery, Clients.All/Caller/Others/Group(), connection management — all in-process.
  • Dual hosting — Same code runs as a browser SPA or a Node.js/Cloudflare Workers/Edge API server.
  • CLI toolingcepha new, cepha dev, cepha publish, cepha kit (Cepha.CLI).

How this complements Blazor

This is not a replacement for Blazor. Blazor excels at rich interactive UIs with fine-grained reactivity. MVC WASM excels at document-oriented apps, admin panels, and CRUD applications where the existing MVC skillset should transfer directly to client-side deployment — without rewriting views as components. Both models share the same .NET WASM runtime and could coexist.


Why This Belongs in .NET 11

  1. The runtime foundation is ready. .NET 10 ships mature WebAssembly support: dotnet.js, Web Workers, JSImport/JSExport, WASM-compiled SQLite. What's missing is the MVC engine layer on top.

  2. NativeAOT for WASM is coming. .NET 11 is expected to advance NativeAOT/Trimming for WebAssembly, which would reduce the current ~15-25MB bundle to a competitive size. This proposal aligns with that timeline.

  3. Edge computing is mainstream. Cloudflare Workers, Deno Deploy, Vercel Edge Functions are standard deployment targets. .NET 11 is the window to establish MVC as a viable edge framework.

  4. Developer demand is real. MVC is the #1 ASP.NET programming model by adoption. Developers want their existing skills to work in new deployment targets — not a new paradigm.

  5. Feasibility is proven. The reference implementation is published on NuGet with CI/CD, covering Controllers → Views → EF Core → Identity → SignalR. This is not theoretical.


What We Are Asking from the .NET Team

We are not asking for a massive new framework — just targeted infrastructure:

Experimental / .NET 11 Preview

  1. Microsoft.NET.Sdk.WebAssembly.Mvc SDK target — embed .cshtml as resources, link SQLite natively, produce static SPA output. (~200 lines of MSBuild in our implementation.)
  2. Lightweight MvcEngine for WASM — controller discovery, route matching, action invocation without the full server pipeline. (Single file in our implementation.)
  3. Razor template provider contract (ITemplateProvider / IRazorTemplateEngine) — rendering .cshtml from embedded resources.
  4. dotnet new mvc-wasm template — scaffolding via the standard template system.

Post-Preview

  1. NativeAOT/Trimming for smaller bundles.
  2. Visual Studio / VS Code F5 debugging in WASM Workers.
  3. Blazor interop — embed MVC views inside Blazor components and vice versa.

What we are NOT asking for: ❌ Changes to the existing MVC server pipeline · ❌ Changes to Blazor · ❌ A new component model · ❌ Deprecation of any existing feature.


Reference Implementation

📦 Repository: github.com/sbay-dev/NetWasmMVCRuntime

NuGet packages:

  • SDK: NetWasmMvc.SDK<Project Sdk="NetWasmMvc.SDK/1.0.6">
  • CLI: Cepha.CLIdotnet tool install --global Cepha.CLI

Key source files: MvcEngine.cs · RazorTemplateEngine.cs · Controller.cs · SignalREngine.cs · Identity/ · CephaApp.cs · main.js · Sdk/


Known .NET Runtime Gaps (Active)

The following issues are currently open in the .NET runtime repositories and directly affect this proposal. Cepha (Cepha.CLI + NetWasmMvc.SDK) was built to bridge these gaps while the .NET team evaluates official support.

🐛 Visual Studio Publish: Empty Output Folder for browser-wasm

When publishing a browser-wasm project from Visual Studio's Publish dialog, the destination folder is completely empty — no _framework/, no wwwroot/, no index.html. The same project publishes correctly via dotnet publish -c Release from the command line.

This is a critical tooling gap: enterprise developers expect Visual Studio's integrated Publish workflow to produce deployable output. Today it produces nothing for WebAssembly MVC projects.

📋 dotnet/runtime #125136 — No Client-Side MVC Runtime

The core issue: .NET provides no SDK target, no MvcEngine, and no Razor template contract for running MVC in WebAssembly. The entire MVC pipeline assumes a server host — there is no Microsoft.NET.Sdk.WebAssembly.Mvc. This issue has been acknowledged and placed in the Future milestone.

💬 dotnet/runtime Discussion #125144 — Community Feedback

The companion discussion seeking community input on whether client-side MVC for WebAssembly should be officially supported. Covers bundle size concerns, NativeAOT timeline, edge deployment interest, and the relationship with Blazor.

  • Discussion: dotnet/runtime #125144
  • Key question: Should this start as an experimental SDK — similar to how Blazor began?

🧬 Cepha: The Bridge

While these issues remain open, Cepha provides a complete solution today: a custom SDK target, a lightweight MvcEngine, a Razor template engine, CLI tooling (cepha dev / cepha publish), and correct dotnet publish output for browser-wasm. Cepha solves today what the .NET team has acknowledged as a gap for .NET 11+.

# Install and start building now
dotnet tool install --global Cepha.CLI
cepha new MyApp
cepha dev

Call for Feedback

We'd love to hear from the community and the .NET team:

  1. Is this worth supporting officially? Do enough developers want MVC in the browser to justify framework-level investment?
  2. Should this start as an experimental SDK? Similar to how Blazor started — let the community validate the model first.
  3. What MVC features are essential for a v1? The current implementation covers Controllers, Views, EF Core, Identity, and SignalR. What else would you need?
  4. Bundle size concerns? The ~15-25MB size is the biggest barrier. Would NativeAOT improvements in .NET 11 make this viable for your use case?
  5. Edge deployment interest? Is running .NET MVC on Cloudflare Workers / Deno Deploy compelling for your team?

If you find this idea interesting, please 👍 this discussion and share your use cases. Community signal helps the .NET team prioritize.


Based on WasmMvcRuntime — an open-source client-side ASP.NET MVC runtime for WebAssembly, published on NuGet and actively maintained.