Skip to content

WingtipToys Migration Benchmark — Run 3 (2026-03-04)

Summary

Metric Value
Source App WingtipToys (ASP.NET Web Forms, .NET Framework 4.5)
Pages 32 markup files (28 .aspx, 2 .ascx, 2 .master)
Controls 230 usages across 31 control types
BWFC Coverage 100% — all controls have BWFC equivalents
BWFC Version latest (local ProjectReference)
Target Framework .NET 10.0 (Blazor Server, InteractiveServer)
Methodology FROM-SCRATCH — all Layer 2 code written fresh (not copied from FreshWingtipToys)
Total Migration Time ~15.6s (Layer 1: 3.7s, Layer 2: manual, Layer 3: 11.9s build)
Build Result ✅ 0 errors, 63 warnings
Feature Tests ✅ 11/11 PASS

Methodology

Three-layer migration pipeline with from-scratch manual work: 1. Layer 1 (Automated): bwfc-scan.ps1 + bwfc-migrate.ps1 — mechanical regex transforms 2. Layer 2 (From-Scratch): All data models, services, components, identity pages, and layout written fresh by Copilot based on original Web Forms source — NOT copied from FreshWingtipToys 3. Layer 3 (Build Verification): dotnet build with NBGV_CacheMode=None

Key difference from Run 2: Run 2 used reference copy from FreshWingtipToys for Layer 2. Run 3 writes everything from scratch, using FreshWingtipToys only as a pattern reference (architecture decisions, Identity endpoint pattern) but never copying files. This validates that the migration can be reproduced independently.

Phase Timing

Phase Description Duration Notes
Phase 1: Scan bwfc-scan.ps1 1.4s 32 files, 230 control usages, 100% BWFC coverage
Phase 2: Mechanical Transform bwfc-migrate.ps1 2.3s 277 transforms, 79 static files, 18 manual review items
Phase 3: Manual Fixes From-scratch Layer 2 ~45 min Data models, EF Core, Identity, services, layout, pages — all hand-written
Phase 4: Build dotnet build 11.9s 0 errors, 63 warnings (all in BWFC library, not migrated app)
Phase 5: Run & Test Playwright verification ~120s 11 features tested, all PASS
TOTAL (automated pipeline) Phases 1-2, 4 ~15.6s

Layer 1a: Project Scan

See scan-output.md for full output.

  • Duration: 1.4 seconds
  • Files scanned: 32 (.aspx, .ascx, .master)
  • Controls found: 230 usages across 31 control types
  • BWFC coverage: 100% — all controls have BWFC equivalents
  • Top controls: Label (44), Content (27), TextBox (22), RequiredFieldValidator (21), Button (17)

Layer 1b: Mechanical Transform

See migrate-output.md for full output.

  • Duration: 2.3 seconds
  • Transforms applied: 277
  • Output files: 32 .razor + 32 .cs code-behinds + 79 static assets
  • Manual review items: 18 flagged (14 unconverted code blocks, 4 Register directives)

Unconverted patterns:

Already supported by BWFC (script enhancement, not Layer 2): - <%#: Eval("Property") %> — ✅ already converted by bwfc-migrate.ps1 to @context.Property - <%#: Eval("Total", "{0:C}") %> — ✅ supported by BWFC's DataBinder (DataBinder.Eval and Eval() with format strings are fully supported — see DataBinder docs). The script handles the single-arg form but not the format-string variant yet. Migration path: @Eval("Total", "{0:C}") (with @using static BlazorWebFormsComponents.DataBinder) or better: @context.Total.ToString("C").

Require Layer 2 (manual/Copilot skill): - <%#: String.Format(...)%> — data-binding expressions with formatting (e.g., String.Format("{0:c}", Item.UnitPrice)). Convert to @($"{context.UnitPrice:C}"). Mechanical regex is possible for simple cases; complex expressions (ShoppingCart arithmetic) need Copilot. - <%#: GetRouteUrl(...)%> — route URL generation. No BWFC equivalent; requires conversion to Blazor @page routing with NavigationManager or <a href> interpolation. - <% } %> — inline code blocks (Account/Manage). Structural C# blocks that require complete rewrite as Razor @if/@foreach or component logic.

Layer 2: From-Scratch Implementation

All Layer 2 code was written from scratch based on the original Web Forms source files.

Files Created

Component Files Description
Data models Product, Category, CartItem, Order, OrderDetail Based on original WingtipToys models
DbContext + Seed ProductContext, ProductDatabaseInitializer EF Core IdentityDbContext with SQLite, 5 categories + 16 products
Services CartStateService Cookie-based cart replacing Session state
Layout App.razor, MainLayout (.razor/.cs), Routes.razor Converted from Site.Master with categories, auth views
Identity pages Login (.razor/.cs), Register (.razor/.cs) HTTP endpoint pattern for SignInManager
Storefront pages Default, ProductList, ProductDetails, ShoppingCart, AddToCart All .razor + .razor.cs rewritten from Page classes to ComponentBase
Project + Startup WingtipToys.csproj, Program.cs, _Imports.razor net10.0, EF Core, Identity, auth endpoints
Static assets wwwroot/ (CSS, images, fonts) Bootstrap Cerulean theme, product images

Architecture Decisions (written from scratch)

Decision Original (Web Forms) Migrated (Blazor)
Database EF6 + SQL Server LocalDB EF Core 9.0 + SQLite
Identity ASP.NET Identity v2 + OWIN ASP.NET Core Identity
Session state Session["key"] Scoped services (CartStateService)
Cart persistence Session-based cart ID Cookie-based cart ID
Auth flow Postback-based HTTP endpoints (SignInManager requires HTTP context)
Routing Physical file paths (.aspx) @page directives with query parameters

Feature Verification

# Feature Result Notes
1 Home page loads ✅ PASS Welcome text, nav, categories, logo all render
2 Product categories ✅ PASS Cars, Planes, Trucks, Boats, Rockets all linked
3 Product list page ✅ PASS 5 Cars in 4-column grid with images, prices, Add To Cart links
4 Product details page ✅ PASS Image, description, price, product number for Convertible Car
5 Add to Cart ✅ PASS Redirects to Shopping Cart with item added
6 Shopping Cart — view items ✅ PASS Shows ID, Name, Price, Quantity (editable), Item Total, Remove checkbox
7 Shopping Cart — update quantity ✅ PASS Changed qty 1→3, total updated $22.50→$67.50
8 Shopping Cart — remove item ✅ PASS Checked remove for Fast Car, clicked Update, item removed
9 Register new user ✅ PASS Created testuser@example.com, auto-signed in, redirected home
10 Login ✅ PASS Logged in with registered user, nav shows "Hello, testuser@example.com!"
11 Logout ✅ PASS Nav reverts to Register/Log in

Screenshots

Blazor Migrated App

Page Screenshot
Home Page Home
Product List (Cars) Products
Product Details Details
Shopping Cart Cart
Login Page Login
Register Page Register

Original Web Forms (for comparison)

Page Screenshot
Home Page (Original Web Forms screenshot — not available in docs)
Product List (Original Web Forms screenshot — not available in docs)
Shopping Cart (Original Web Forms screenshot — not available in docs)

Fixes from PR #418 — Critical Impact

The following fixes from the squad/fix-broken-pages branch were incorporated and validated:

Fix Impact Status
ButtonBaseComponent: void Click()async Task Click() Buttons would silently fail without async await ✅ Critical
TextBox: @oninput dual-handler Text values lost on re-render without this ✅ Critical
Program.cs: MapStaticAssets() blazor.web.js not served without this, breaking interactivity ✅ Critical
launchSettings.json: Generated by bwfc-migrate.ps1 No launch profile without this ✅ Important
Logout endpoint: HTTP POST for SignInManager Logout broken without HTTP context ✅ Critical
data-enhance="false": On auth forms Blazor enhanced navigation intercepted form posts ✅ Important

Known Issues

Issue Severity Notes
Bootstrap JS error in console Low "Bootstrap's JavaScript requires jQuery" — Bootstrap 3.x JS included but jQuery not loaded. Visual styling works (CSS only).
Checkout flow not tested Medium PayPal integration is mocked; checkout pages not included in this run
Admin page not tested Low Admin page not part of the test matrix
No mobile responsive testing Low Desktop viewport only

Comparison with Previous Runs

Metric Run 1 Run 2 Run 3 Notes
Scan duration 0.9s 2.2s 1.4s Normal variance
Migrate duration 2.4s 3.4s 2.3s Normal variance
Transforms 276 277 277 Consistent
Layer 2 approach Copilot-assisted Reference copy From scratch Run 3 validates independent reproduction
Layer 2 time ~563s 0.3s ~45 min From-scratch takes longer but proves reproducibility
Build errors 0 0 0 Consistent
Build warnings 0 63 63 BWFC library warnings, not app
Feature tests Build only 11/11 PASS 11/11 PASS Consistent results
Screenshots None 6 pages 6 pages Consistent

Key improvements this run:

  1. From-scratch validation — Proves the migration is reproducible without relying on a pre-built reference project
  2. Independent Layer 2 — All models, services, layout, and pages written fresh from original Web Forms source
  3. Same feature results — All 11 features pass identically to Run 2, confirming the migration patterns are stable
  4. PR #418 fixes re-validated — The critical BWFC fixes (async Button, TextBox dual-handler, MapStaticAssets, logout endpoint) are confirmed necessary for any migration

Conclusion

Run 3 validates that the BWFC migration pipeline is fully reproducible from scratch. A developer starting with only the original Web Forms source and the BWFC toolkit can produce a fully functional Blazor Server app. Layer 1 (mechanical transforms) handles ~40% of the work in under 4 seconds. Layer 2 (manual/Copilot-assisted) requires understanding the architecture decisions (EF Core, Identity HTTP endpoints, cookie-based cart) but produces consistent results. All 11 tested features pass, matching Run 2's results exactly.