Skip to content

Commit e4ba87d

Browse files
csharpfritzCopilot
andcommitted
feat(cli): compile page code-behind safely
Merge the page runtime shim, emit compile-safe page code-behind into the generated app, and add explicit BWFC global usings for local project-reference scaffolds. Include focused test updates plus Wingtip run29-run31 reports documenting the benchmark progression. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent a28d88a commit e4ba87d

15 files changed

Lines changed: 593 additions & 273 deletions

File tree

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
# WingtipToys Migration Test - Run 29
2+
3+
**Date:** 2026-04-28 17:23:32 -04:00
4+
**Branch:** `feature/wingtip-next-features-review`
5+
**Operator:** Copilot
6+
**Requested by:** user
7+
8+
---
9+
10+
## Summary
11+
12+
| Metric | Value |
13+
|--------|-------|
14+
| Source project | `samples/WingtipToys/WingtipToys` |
15+
| Output project | `samples/AfterWingtipToys` |
16+
| Toolkit entry point | `migration-toolkit/scripts/bwfc-migrate.ps1` |
17+
| Report folder | `dev-docs/migration-tests/wingtiptoys/run29` |
18+
| Total wall-clock time | `~00:02:47` |
19+
| Build result | `Failed: 183 errors, 54 warnings` |
20+
| Acceptance tests | `Not run; blocked by build failure` |
21+
| Final status | `FAILED` |
22+
23+
## Executive Summary
24+
25+
Run 29 was a valid fresh benchmark run from `samples\WingtipToys\WingtipToys` through the toolkit wrapper into a cleared `samples\AfterWingtipToys`, but it did not reach the acceptance bar. The toolkit correctly resolved the nested Wingtip source root, produced a full scaffold with static assets and quarantined legacy compile-surface artifacts, then stopped at a large compile break where several routed pages still depended on quarantined code-behind members and unresolved Web Forms constructs.
26+
27+
## Timing
28+
29+
| Phase | Duration | Notes |
30+
|-------|----------|-------|
31+
| Preparation | `00:00:00.19` | Run numbering, folder cleanup, report folder creation |
32+
| Layer 1 toolkit migration | `00:00:14.72` | `bwfc-migrate.ps1` invocation |
33+
| Repair / migration skill work | `00:02:27` | Failure analysis and toolkit-gap triage only; no successful in-place recovery |
34+
| Build validation | `00:00:04.70` | First build of fresh output |
35+
| Acceptance tests | `00:00:00` | Blocked by build failure |
36+
| Screenshots + report | `00:00:00` | Screenshots blocked because the app never became runnable |
37+
| **Total** | `~00:02:47` | |
38+
39+
## Commands
40+
41+
```powershell
42+
# Clear output
43+
Get-ChildItem samples\AfterWingtipToys -Force | Remove-Item -Recurse -Force
44+
45+
# Run migration toolkit
46+
pwsh -File migration-toolkit\scripts\bwfc-migrate.ps1 -Path samples\WingtipToys -Output samples\AfterWingtipToys -Verbose
47+
48+
# Build
49+
dotnet build samples\AfterWingtipToys\WingtipToys.csproj --nologo
50+
```
51+
52+
## What Worked Well
53+
54+
1. The toolkit correctly **resolved the nested effective app root**: `samples\WingtipToys\WingtipToys`.
55+
2. Layer 1 produced a **complete scaffold plus migrated page set**: `32` files processed and `176` files written.
56+
3. Static assets and legacy infrastructure handling improved materially: `80` static files copied, `6` compile-surface artifacts quarantined, and `4` App_Start files quarantined instead of poisoning the build immediately.
57+
58+
## What Didn't Work Well
59+
60+
1. Several routed pages still render markup that depends on **quarantined code-behind members** rather than a compiled partial class or a completed semantic rewrite.
61+
2. The generated master shell still contains unresolved **Web Forms runtime constructs** such as `Scripts.Render`, `webopt:bundlereference`, `HttpContext.Current`, `GetUserName()`, and undeclared chrome elements like `adminLink` / `cartCount`.
62+
3. Validator-heavy account pages still hit **generic inference / markup normalization failures** (`RZ10001`) instead of producing acceptance-ready SSR forms.
63+
64+
## Build Result
65+
66+
The first build of the freshly generated app failed:
67+
68+
- **Result:** `FAILED`
69+
- **Errors:** `183`
70+
- **Warnings:** `54`
71+
72+
The dominant error classes were:
73+
74+
1. **Missing code-behind members in compiled pages** (`CS0103`)
75+
Examples: `Site.razor`, `ProductDetails.razor`, `ShoppingCart.razor`, and `Admin\AdminPage.razor` still reference methods, refs, or fields that only exist in `migration-artifacts\codebehind\*.txt`.
76+
2. **Unnormalized Web Forms data-binding constructs** (`CS0103`, `CS1061`, `CS1662`)
77+
Examples: `ShoppingCart.razor` still contains `Item`-style template expressions and object-typed fields that no longer line up with the generated templated controls.
78+
3. **Validator/component generic inference failures** (`RZ10001`)
79+
Account pages such as `Account\AddPhoneNumber.razor` and `Account\Forgot.razor` still emit validators that Razor cannot infer cleanly.
80+
4. **Master-shell runtime drift**
81+
`Site.razor` still mixes preserved shell markup with APIs and members that were not normalized into runnable Blazor/BWFC equivalents.
82+
83+
## Acceptance Test Result
84+
85+
| Metric | Value |
86+
|--------|-------|
87+
| Total | `0` |
88+
| Passed | `0` |
89+
| Failed | `0` |
90+
| Skipped | `0` |
91+
92+
Acceptance tests were **not run** because `samples\AfterWingtipToys\WingtipToys.csproj` did not build. This run does not meet the benchmark success criteria.
93+
94+
## Toolkit Gaps Exposed by This Run
95+
96+
1. **Code-behind recovery is still incomplete for benchmark-critical pages.**
97+
The toolkit quarantines code-behind correctly, but it still leaves generated markup in `Site`, `ProductDetails`, `ShoppingCart`, and `AdminPage` depending on members that never get reintroduced as runnable Blazor code.
98+
2. **Master-page shell migration still needs a Wingtip-specific hardening pass.**
99+
The shell contract is present, but the generated `Site.razor` still preserves unresolved bundles, OWIN/auth hooks, and HTML elements whose behavior was previously supplied by master-page code-behind.
100+
3. **Data-bound action/detail pages still need deeper semantic normalization.**
101+
`FormView`, `GridView`, and template output still carry Web Forms expressions, row-oriented control lookup, and event wiring that are not yet converted into acceptance-ready SSR patterns.
102+
4. **Account-form migration still needs validator and event normalization.**
103+
The toolkit now recognizes account pages better, but validator-heavy forms still stop at compile-time rather than migrating into a buildable SSR shape.
104+
105+
## Screenshot Gallery
106+
107+
No screenshots were captured for this run because the migrated app never reached a runnable state.
108+
109+
## Notes
110+
111+
- Layer 1 evidence is captured in `migrate-output.md`.
112+
- Build failure evidence is captured in `build-output.md`.
113+
- Migration summary from Layer 1:
114+
- `Resolved source root: D:\BlazorWebFormsComponents\samples\WingtipToys\WingtipToys`
115+
- `Files processed: 32`
116+
- `Files written: 176`
117+
- `Static files copied: 80`
118+
- `Source files copied: 9`
119+
- `Compile-surface artifacts quarantined: 6`
120+
- `App_Start files quarantined: 4`
121+
- This run is still useful: it confirms the toolkit is substantially better at scaffolding and compile-surface isolation, but the next feature work should focus on the remaining **runnable page semantics** rather than more scaffolding changes.
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
# WingtipToys Migration Run 30
2+
3+
## Summary
4+
5+
Run 30 validated the new **compiled page code-behind path** and the merged page runtime shim. The migration still does not build cleanly, but the generated WingtipToys app dropped from **183 build errors in run29 to 60 build errors in run30**.
6+
7+
The important behavior change is that the CLI no longer quarantines every page code-behind file. In this run it emitted **20 compiled `.razor.cs` files** and quarantined only **12 page/user-control/master-page code-behind artifacts** that still contain unsupported patterns.
8+
9+
## Migration Result
10+
11+
- Files processed: **32**
12+
- Files written: **176**
13+
- Static files copied: **80**
14+
- Source files copied: **9**
15+
- Compile-surface artifacts quarantined: **6**
16+
- App_Start files quarantined: **4**
17+
18+
## Build Result
19+
20+
- Build target: `samples\AfterWingtipToys\WingtipToys.csproj`
21+
- Result: **failed**
22+
- Warnings: **23**
23+
- Errors: **60**
24+
25+
## What Improved
26+
27+
1. Shim-compatible pages now emit real compiled code-behind files instead of always landing in `migration-artifacts\codebehind\`.
28+
2. The merged `Page` / `WebFormsPageBase` runtime keeps head rendering on the shared page shim surface without forcing layout wrappers to duplicate page-service logic.
29+
3. The fresh Wingtip run now compiles far enough that the dominant failures are narrower and more actionable than the run29 “missing page members everywhere” failure wall.
30+
31+
## Remaining Top Gaps
32+
33+
1. **Missing generated usings/type imports in emitted `.razor.cs` files**
34+
Several compiled page partials now fail on BWFC control types such as `Button`, `Label`, `Panel`, `GridView<>`, `FormView<>`, and `RequiredFieldValidator<>`. The compiled-codebehind path needs companion using/import normalization.
35+
36+
2. **Legacy page-model attributes still leak into compiled code-behind**
37+
`ProductDetails.razor.cs` still contains Web Forms model-binding attributes like `[QueryString]` and ambiguous `RouteData` references. Those need transform-time normalization before emission.
38+
39+
3. **Validator inference and templated markup normalization are still incomplete**
40+
Account pages still fail with `RZ10001` for `RequiredFieldValidator`, and pages like `CheckoutReview.razor` / `ProductList.razor` still contain invalid templated markup shapes.
41+
42+
4. **Unresolved server-block markup still escapes into generated pages**
43+
`Manage.razor`, `ProductList.razor`, and `ShoppingCart.razor` still contain `%`/`<%#:` fragments or malformed HTML. Those pages were correctly quarantined or left uncompiled, but the markup transforms still need stronger cleanup.
44+
45+
5. **Master-page shell still leaves unresolved script-manager style markup**
46+
`Site.razor` now avoids compile-surface code-behind failure, but it still carries unresolved `Scripts` / `ScriptReference` markup that blocks the generated app build.
47+
48+
## Conclusion
49+
50+
Run 30 confirms that the new page-codebehind architecture is worth keeping: it materially reduces the Wingtip compile surface failure count and exposes the next concentrated gaps. The next follow-up should focus on **compiled code-behind import normalization** plus the existing **validator/template/server-block markup** cleanup work.
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
# WingtipToys Migration Run 31
2+
3+
## Summary
4+
5+
Run 31 validated the **compiled page imports fix**. The generated scaffold now writes BWFC global usings into `GlobalUsings.cs`, which means emitted page `.razor.cs` files no longer depend on NuGet-only package targets to resolve BWFC component types.
6+
7+
That fix worked for the original target bucket: the deduplicated `CS0246` failures dropped from **41 in run30 to 7 in run31**. The remaining `CS0246` errors are no longer the broad page-control import problem; they are now limited to deeper support-code issues such as `HttpException` and copied identity/application types in `Logic\RoleActions.cs`.
8+
9+
## Migration Result
10+
11+
- Files processed: **32**
12+
- Files written: **176**
13+
- Compile-surface artifacts quarantined: **6**
14+
- App_Start files quarantined: **4**
15+
16+
## Build Result
17+
18+
- Build target: `samples\AfterWingtipToys\WingtipToys.csproj`
19+
- Result: **failed**
20+
- Reported build totals: **205 errors**, **112 warnings**
21+
22+
## What Improved
23+
24+
1. `GlobalUsings.cs` now includes:
25+
- `global using BlazorWebFormsComponents;`
26+
- `global using BlazorWebFormsComponents.Enums;`
27+
- `global using BlazorWebFormsComponents.LoginControls;`
28+
- `global using BlazorWebFormsComponents.Validations;`
29+
2. Emitted page code-behind no longer fails en masse on missing `Button`, `Label`, `TextBox`, `GridView<T>`, `FormView<T>`, `RequiredFieldValidator<T>`, and similar BWFC types.
30+
3. The prior “compiled page imports” bucket is now effectively cleared, exposing the next true blockers.
31+
32+
## New Dominant Failure Classes
33+
34+
Deduplicated run31 errors are now dominated by:
35+
36+
1. **Missing members / semantic migration gaps (`CS0103`)** — 71 errors
37+
Generated markup and code-behind still disagree on members/events in pages like `CheckoutReview`, `CheckoutComplete`, `ShoppingCart`, `Site`, and account flows.
38+
39+
2. **Analyzer/style noise promoted to errors (`IDE0007`)** — 70 errors
40+
Copied support files such as `Logic\PayPalFunctions.cs` now enter the build and fail on style analyzers, which should not be a migration blocker.
41+
42+
3. **API/runtime surface mismatches (`CS1061`, `CS0234`, `CS1929`, etc.)**
43+
These now stand out more clearly in copied support code and identity-related logic.
44+
45+
4. **Validator inference and markup cleanup remain**
46+
The existing `RZ10001`, `RZ9980`, `RZ9981`, and `RZ9996` buckets are still present and unchanged from run30.
47+
48+
## Conclusion
49+
50+
Run 31 confirms the imports fix should be kept. It solved the specific page-codebehind namespace problem and moved the benchmark forward to the next layers:
51+
52+
1. semantic member alignment between generated markup and emitted code-behind,
53+
2. compile-surface filtering or warning suppression for copied support code,
54+
3. validator/template/server-block markup cleanup.

docs/UtilityFeatures/PageService.md

Lines changed: 17 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -32,9 +32,9 @@ The Page system uses three complementary pieces:
3232

3333
```mermaid
3434
flowchart LR
35-
A["<b>WebFormsPageBase</b>\n<i>(code-behind API)</i>\n\n• Title\n• MetaDescription\n• MetaKeywords\n• IsPostBack\n• Page (self-ref)"]
35+
A["<b>WebFormsPageBase</b>\n<i>(shared page shim)</i>\n\n• Title / Meta*\n• IsPostBack\n• Request / Response\n• Session / Cache\n• ClientScript / ViewState"]
3636
B["<b>IPageService</b>\n<i>(scoped bridge)</i>\n\n• Title\n• MetaDescription\n• MetaKeywords\n• Change events"]
37-
C["<b>WebFormsPage</b>\n<i>(layout wrapper)</i>\n\n• &lt;PageTitle&gt;\n• &lt;meta&gt; tags\n• NamingContainer\n• ThemeProvider"]
37+
C["<b>Page + WebFormsPage</b>\n<i>(renderers)</i>\n\n• &lt;PageTitle&gt;\n• &lt;meta&gt; tags\n• NamingContainer\n• ThemeProvider"]
3838
3939
A -->|writes| B -->|reads| C
4040
@@ -46,14 +46,15 @@ flowchart LR
4646

4747
| Piece | Role | Where it lives |
4848
|---|---|---|
49-
| **`WebFormsPageBase`** | Abstract base class for converted pages. Provides `Title`, `MetaDescription`, `MetaKeywords`, `IsPostBack`, and `Page` (self-reference). This is the **code-behind API** your pages inherit. | `@inherits` in `_Imports.razor` |
49+
| **`WebFormsPageBase`** | Abstract base class for converted pages and the shared runtime shim behind `<Page />`. Provides `Title`, `MetaDescription`, `MetaKeywords`, `IsPostBack`, `Page`, `Request`, `Response`, `Session`, `Server`, `Cache`, `ClientScript`, and `ViewState`. | `@inherits` in `_Imports.razor`, inherited by `<Page />` |
5050
| **`IPageService` / `PageService`** | Scoped service that bridges the base class and the renderer. Property setters fire change events. | Registered by `AddBlazorWebFormsComponents()` |
51-
| **`WebFormsPage`** | Unified layout wrapper that provides NamingContainer (ID mangling), ThemeProvider (skin cascading), AND page head rendering (`<PageTitle>` + `<meta>` tags). Subscribes to `IPageService` events. | `<WebFormsPage>` wrapping `@Body` in layout |
51+
| **`Page`** | Head renderer that inherits `WebFormsPageBase`, reads the current title/meta values, and disables postback interop because it only renders `<PageTitle>` and `<meta>` tags. | `<Page />` |
52+
| **`WebFormsPage`** | Unified layout wrapper that provides NamingContainer (ID mangling), ThemeProvider (skin cascading), and optionally composes `<Page />` for head rendering. | `<WebFormsPage>` wrapping `@Body` in layout |
5253

53-
**Key point:** `WebFormsPageBase` and `WebFormsPage` are complementary, not redundant. `WebFormsPageBase` *writes* data to `IPageService`; `WebFormsPage` *reads* from `IPageService` and renders HTML. Both are needed.
54+
**Key point:** `WebFormsPageBase` is now the shared page shim. Converted pages inherit it directly, `<Page />` reuses it for head rendering, and `<WebFormsPage>` composes `<Page />` instead of duplicating page-service subscription logic.
5455

55-
!!! tip "All-in-one layout component"
56-
`<WebFormsPage>` combines naming, theming, and head rendering into a single component. You no longer need a separate `<Page />` component — `<WebFormsPage>` handles everything.
56+
!!! tip "Default layout pattern"
57+
`<WebFormsPage>` is still the simplest layout wrapper. It now renders `<Page />` internally by default, so you get naming, theming, and page-head rendering from one component.
5758

5859
## One-Time Setup
5960

@@ -86,10 +87,7 @@ This registers `IPageService` as a scoped service.
8687
- **Page head rendering** — Subscribes to `IPageService` and renders `<PageTitle>` + `<meta>` tags
8788

8889
!!! note "RenderPageHead parameter"
89-
If you need to handle head rendering separately (e.g., with a standalone `<Page />` component), set `RenderPageHead="false"` on `<WebFormsPage>` to disable head rendering.
90-
91-
!!! note "Standalone Page.razor"
92-
The `<BlazorWebFormsComponents.Page />` component is still available as a standalone head renderer for apps that don't use `<WebFormsPage>`. However, when using `<WebFormsPage>`, the standalone `<Page />` is unnecessary — don't use both, or you'll get duplicate head content.
90+
If you need to handle head rendering separately, set `RenderPageHead="false"` on `<WebFormsPage>` and place `<Page />` yourself. Otherwise, let `<WebFormsPage>` own that composition to avoid duplicate head tags.
9391

9492
## Primary Approach: WebFormsPageBase
9593

@@ -131,7 +129,7 @@ With `WebFormsPageBase` as your base class, Web Forms code-behind patterns work
131129
This works because:
132130

133131
- **`Page`** returns `this` (a self-reference), so `Page.Title` resolves to `this.Title`
134-
- **`Title`**, **`MetaDescription`**, and **`MetaKeywords`** delegate to the injected `IPageService`
132+
- **`Title`**, **`MetaDescription`**, and **`MetaKeywords`** delegate to the scoped `IPageService` when available
135133
- **`IsPostBack`** always returns `false` — so `if (!IsPostBack)` blocks always execute, matching first-load behavior
136134

137135
### Properties Available
@@ -143,16 +141,13 @@ This works because:
143141
| `MetaKeywords` | `string` | Gets/sets the meta keywords. Delegates to `IPageService.MetaKeywords`. |
144142
| `IsPostBack` | `bool` | Always returns `false`. Blazor has no postback model. |
145143
| `Page` | `WebFormsPageBase` | Returns `this`. Enables `Page.Title` syntax. |
146-
147-
### Properties NOT Available
148-
149-
The following `System.Web.UI.Page` members are **deliberately omitted** to encourage proper Blazor migration:
150-
151-
- **`Page.Request`** — Use `NavigationManager` or `HttpContext` instead
152-
- **`Page.Response`** — Use `NavigationManager.NavigateTo()` for redirects
153-
- **`Page.Session`** — Use `ProtectedSessionStorage`, `ProtectedLocalStorage`, or a scoped service
154-
- **`Page.Server`** — Use standard .NET APIs (`Path`, `HttpUtility`, etc.)
155-
- **`Page.Cache`** — Use `IMemoryCache` or `IDistributedCache`
144+
| `Request` | `RequestShim` | Compatibility wrapper for query string, cookies, URL, and form data. |
145+
| `Response` | `ResponseShim` | Compatibility wrapper for redirects and cookies. |
146+
| `Session` | `SessionShim` | Dictionary-style session storage with SSR and in-memory fallback behavior. |
147+
| `Server` | `ServerShim` | Compatibility wrapper for `MapPath`, HTML encoding, and URL encoding. |
148+
| `Cache` | `CacheShim` | Compatibility wrapper over `IMemoryCache`. |
149+
| `ClientScript` | `ClientScriptShim` | Registers startup scripts, client script blocks, and postback helpers. |
150+
| `ViewState` | `ViewStateDictionary` | Per-page ViewState-style dictionary. |
156151

157152
!!! warning "Dead Code: `if (IsPostBack)`"
158153
Code guarded by `if (IsPostBack)` (without `!`) will **never execute** because `IsPostBack` is always `false`. During migration, search for `if (IsPostBack)` (without the negation) and flag those blocks for review — they likely contain logic that needs to be reimplemented as Blazor event handlers.

0 commit comments

Comments
 (0)