Skip to content

Commit 25f2d36

Browse files
csharpfritzCopilot
andcommitted
Add webforms-runner skill, fix migration scripts, update Run 06 report
- Add webforms-runner skill for IIS Express + Playwright screenshots - Fix OnClick conversion in bwfc-migrate.ps1 (keep OnClick for BWFC Button) - Add AddHttpContextAccessor to Layer 2 Program.cs generation - Update migration-standards skill with BwfcProjectPath, database provider rules - Add unsupported controls section to Run 06 report Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent d833105 commit 25f2d36

6 files changed

Lines changed: 305 additions & 12 deletions

File tree

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
### 2026-03-09: Migration tooling directives
2+
**By:** Jeff Fritz (via Copilot)
3+
**What:**
4+
1. **Project reference argument** — Always use `-BwfcProjectPath` when running migrations in test mode
5+
2. **OnClick pattern** — Follow documented pattern (BWFC Button uses `OnClick` not `@onclick`)
6+
3. **Database provider** — Strong preference to maintain same provider (e.g., localdb) unless explicitly directed otherwise
7+
4. **EF6 → EF Core cleanup** — Need post-migration cleanup scripts
8+
5. **Unsupported controls** — Always list in migration reports for library prioritization
9+
6. **AddHttpContextAccessor** — ALWAYS add to Program.cs in every migration
10+
7. **ItemType parameter** — Investigate what's actually wrong (GridView/BoundField need explicit type)
11+
12+
**Why:** Run 06 revealed script gaps that should have been caught. These are permanent rules for the migration toolkit.
Lines changed: 228 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,228 @@
1+
---
2+
name: webforms-runner
3+
description: "**WORKFLOW SKILL** - Starts ASP.NET Web Forms applications using IIS Express and captures screenshots with Playwright. USE FOR: running WebForms samples, capturing 'before' screenshots for migration documentation, testing original Web Forms applications. DO NOT USE FOR: running Blazor apps (use dotnet run), running .NET Core apps (use dotnet CLI). INVOKES: IIS Express CLI, Playwright MCP browser tools."
4+
---
5+
6+
# WebForms Runner Skill
7+
8+
This skill enables running ASP.NET Web Forms applications using IIS Express and capturing screenshots with Playwright for documentation purposes.
9+
10+
## Prerequisites
11+
12+
- **IIS Express** installed (typically at `C:\Program Files (x86)\IIS Express\iisexpress.exe`)
13+
- **Visual Studio Build Tools** or **Visual Studio** (for MSBuild)
14+
- **.NET Framework 4.x** installed
15+
- **Playwright MCP tools** available (browser_navigate, browser_snapshot, browser_take_screenshot)
16+
17+
## Available Web Forms Samples
18+
19+
This repository contains two Web Forms sample applications:
20+
21+
| Sample | Path | Port | Database |
22+
|--------|------|------|----------|
23+
| **ContosoUniversity** | `samples/ContosoUniversity/ContosoUniversity/` | 52477 | SQL Server LocalDB |
24+
| **WingtipToys** | `samples/WingtipToys/WingtipToys/` | 44300 | SQL Server LocalDB |
25+
26+
## Starting a Web Forms Application
27+
28+
### Step 1: Build the Project
29+
30+
Web Forms projects require MSBuild (not `dotnet build`):
31+
32+
```powershell
33+
# ContosoUniversity
34+
msbuild "samples\ContosoUniversity\ContosoUniversity\ContosoUniversity.csproj" /p:Configuration=Debug /t:Build /v:m
35+
36+
# WingtipToys
37+
msbuild "samples\WingtipToys\WingtipToys\WingtipToys.csproj" /p:Configuration=Debug /t:Build /v:m
38+
```
39+
40+
If NuGet packages are missing, restore first:
41+
```powershell
42+
nuget restore "samples\ContosoUniversity\ContosoUniversity.sln"
43+
```
44+
45+
### Step 2: Start IIS Express
46+
47+
Use the `/path` option to run directly from the project folder:
48+
49+
```powershell
50+
# ContosoUniversity (port 52477)
51+
& "C:\Program Files (x86)\IIS Express\iisexpress.exe" /path:"D:\BlazorWebFormsComponents\samples\ContosoUniversity\ContosoUniversity" /port:52477
52+
53+
# WingtipToys (port 44300)
54+
& "C:\Program Files (x86)\IIS Express\iisexpress.exe" /path:"D:\BlazorWebFormsComponents\samples\WingtipToys\WingtipToys" /port:44300
55+
```
56+
57+
**Important:** Run IIS Express in async mode with `detach: true` since it's a server process:
58+
```
59+
mode: "async", detach: true
60+
```
61+
62+
### Step 3: Verify the Site is Running
63+
64+
Wait 3-5 seconds for startup, then check with:
65+
```powershell
66+
curl http://localhost:52477 -UseBasicParsing | Select-Object -ExpandProperty StatusCode
67+
```
68+
69+
## IIS Express Command Reference
70+
71+
```
72+
iisexpress.exe [options]
73+
74+
Key options:
75+
/path:app-path Physical path to the application folder
76+
/port:port-number Port to bind (default: 8080)
77+
/clr:clr-version .NET Framework version (e.g., v4.0)
78+
/systray:true|false Show system tray icon (default: true)
79+
```
80+
81+
## Capturing Screenshots with Playwright
82+
83+
Use the Playwright MCP tools to capture screenshots of the running Web Forms application.
84+
85+
### Workflow for Documentation Screenshots
86+
87+
1. **Navigate to the page:**
88+
```
89+
playwright-browser_navigate: { url: "http://localhost:52477/Home.aspx" }
90+
```
91+
92+
2. **Wait for page load:**
93+
```
94+
playwright-browser_wait_for: { time: 2 }
95+
```
96+
97+
3. **Take a snapshot (for accessibility tree):**
98+
```
99+
playwright-browser_snapshot: {}
100+
```
101+
102+
4. **Take a screenshot:**
103+
```
104+
playwright-browser_take_screenshot: {
105+
filename: "dev-docs/screenshots/webforms-home.png",
106+
type: "png"
107+
}
108+
```
109+
110+
### ContosoUniversity Pages
111+
112+
| Page | URL | Screenshot Name |
113+
|------|-----|-----------------|
114+
| Home | `/Home.aspx` | `contoso-webforms-home.png` |
115+
| Students | `/Students.aspx` | `contoso-webforms-students.png` |
116+
| Courses | `/Courses.aspx` | `contoso-webforms-courses.png` |
117+
| Instructors | `/Instructors.aspx` | `contoso-webforms-instructors.png` |
118+
| About | `/About.aspx` | `contoso-webforms-about.png` |
119+
120+
### WingtipToys Pages
121+
122+
| Page | URL | Screenshot Name |
123+
|------|-----|-----------------|
124+
| Home | `/Default.aspx` | `wingtip-webforms-home.png` |
125+
| Products | `/ProductList.aspx?ProductCategory=1` | `wingtip-webforms-products.png` |
126+
| Product Detail | `/ProductDetails.aspx?ProductID=1` | `wingtip-webforms-detail.png` |
127+
| Cart | `/ShoppingCart.aspx` | `wingtip-webforms-cart.png` |
128+
129+
### Screenshot Best Practices
130+
131+
1. **Use consistent naming:** `{app}-webforms-{page}.png` for before, `{app}-blazor-{page}.png` for after
132+
2. **Save to documentation folder:** `dev-docs/migration-tests/{run}-screenshots/`
133+
3. **Capture full page when needed:** Use `fullPage: true` for long pages
134+
4. **Wait for dynamic content:** Add `browser_wait_for` before screenshots if page has AJAX
135+
136+
## Complete Example: ContosoUniversity Screenshots
137+
138+
```powershell
139+
# 1. Build the project
140+
msbuild "samples\ContosoUniversity\ContosoUniversity\ContosoUniversity.csproj" /p:Configuration=Debug /t:Build /v:m
141+
142+
# 2. Start IIS Express (async, detached)
143+
# Use powershell tool with mode: "async", detach: true
144+
& "C:\Program Files (x86)\IIS Express\iisexpress.exe" /path:"D:\BlazorWebFormsComponents\samples\ContosoUniversity\ContosoUniversity" /port:52477
145+
146+
# 3. Wait for startup
147+
Start-Sleep -Seconds 5
148+
149+
# 4. Use Playwright MCP tools to capture screenshots:
150+
# - browser_navigate to http://localhost:52477/Home.aspx
151+
# - browser_wait_for time: 2
152+
# - browser_take_screenshot filename: "dev-docs/contoso-screenshots/webforms-home.png"
153+
# - Repeat for each page
154+
155+
# 5. Stop IIS Express when done
156+
# Find the process ID from the async shell output
157+
Stop-Process -Id <PID>
158+
```
159+
160+
## Stopping IIS Express
161+
162+
Since IIS Express runs as a detached process, you need to stop it by PID:
163+
164+
```powershell
165+
# Find IIS Express processes
166+
Get-Process | Where-Object { $_.ProcessName -eq "iisexpress" }
167+
168+
# Stop by specific PID
169+
Stop-Process -Id <PID>
170+
171+
# Or stop all IIS Express instances
172+
Get-Process -Name "iisexpress" -ErrorAction SilentlyContinue | Stop-Process
173+
```
174+
175+
## Troubleshooting
176+
177+
### Port Already in Use
178+
```powershell
179+
# Check what's using the port
180+
Get-NetTCPConnection -LocalPort 52477 -ErrorAction SilentlyContinue
181+
182+
# Kill the process
183+
Stop-Process -Id (Get-NetTCPConnection -LocalPort 52477).OwningProcess
184+
```
185+
186+
### Missing NuGet Packages
187+
```powershell
188+
# Restore packages for the solution
189+
nuget restore "samples\ContosoUniversity\ContosoUniversity.sln"
190+
```
191+
192+
### Database Connection Issues
193+
- Ensure SQL Server LocalDB is installed and running
194+
- Check connection string in `Web.config`
195+
- LocalDB instances can be started with: `sqllocaldb start MSSQLLocalDB`
196+
197+
### MSBuild Not Found
198+
Add Visual Studio MSBuild to PATH or use full path:
199+
```powershell
200+
& "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\MSBuild\Current\Bin\MSBuild.exe" project.csproj
201+
```
202+
203+
## Side-by-Side Screenshots for Migration Documentation
204+
205+
When creating migration documentation, capture both WebForms and Blazor screenshots:
206+
207+
```
208+
dev-docs/migration-tests/wingtiptoys-run15-screenshots/
209+
├── webforms-home.png # Before (WebForms)
210+
├── blazor-home.png # After (Blazor)
211+
├── webforms-products.png
212+
├── blazor-products.png
213+
└── ...
214+
```
215+
216+
This allows creating visual comparisons in migration reports showing the fidelity of the conversion.
217+
218+
## Integration with Migration Workflow
219+
220+
1. **Before migration:** Start WebForms app, capture baseline screenshots
221+
2. **Run migration:** Execute bwfc-migrate.ps1 and bwfc-migrate-layer2.ps1
222+
3. **After migration:** Start Blazor app (`dotnet run`), capture result screenshots
223+
4. **Document:** Create side-by-side comparison in migration report
224+
225+
## Related Skills
226+
227+
- **webforms-migration** - For migrating ASPX/ASCX files to Blazor
228+
- **documentation** - For writing migration reports with screenshots

dev-docs/migration-tests/contoso-run06.md

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,50 @@ Run 05 documentation updated with Blazor implementation screenshots:
116116
- `dev-docs/migration-tests/contoso-run05-screenshots/` — 5 Blazor screenshots
117117
- This report
118118

119+
## Unsupported Controls Encountered
120+
121+
The following Web Forms controls are used in ContosoUniversity but are **not supported** by BlazorWebFormsComponents. These should be prioritized for potential library additions or documented as requiring manual migration.
122+
123+
### `<ajaxToolkit:AutoCompleteExtender>`
124+
125+
| Attribute | Value |
126+
|-----------|-------|
127+
| **Used In** | Students.aspx, Courses.aspx |
128+
| **Purpose** | Provides typeahead/autocomplete functionality for TextBox controls. Calls a server-side `ServiceMethod` to fetch suggestions as the user types. |
129+
| **Configuration** | `MinimumPrefixLength="1"`, `CompletionInterval="100"`, `EnableCaching="true"`, `CompletionSetCount="20"` |
130+
| **Blazor Alternative** | Use a custom autocomplete component with `@oninput` event, debouncing, and async data fetching. Consider third-party libraries like MudBlazor `<MudAutocomplete>` or Radzen `<RadzenAutoComplete>`. |
131+
132+
### `<asp:ScriptManager>`
133+
134+
| Attribute | Value |
135+
|-----------|-------|
136+
| **Used In** | Students.aspx, Courses.aspx, Instructors.aspx |
137+
| **Purpose** | Required by ASP.NET AJAX to manage client script libraries, partial page rendering, and client proxy generation for web services. |
138+
| **Blazor Alternative** | **Not needed.** Blazor handles all JavaScript interop and component updates natively. Simply remove this control during migration. |
139+
140+
### `<asp:UpdatePanel>`
141+
142+
| Attribute | Value |
143+
|-----------|-------|
144+
| **Used In** | Students.aspx, Courses.aspx, Instructors.aspx |
145+
| **Purpose** | Enables partial page updates without full postbacks. Content inside `<ContentTemplate>` is updated asynchronously via AJAX. |
146+
| **Blazor Alternative** | **Not needed.** Blazor's component model handles partial updates automatically. Any state change triggers a re-render of only the affected components. Remove the UpdatePanel wrapper and migrate the inner content directly. |
147+
148+
### Summary Table
149+
150+
| Control | Pages | Priority | Recommendation |
151+
|---------|-------|----------|----------------|
152+
| `AutoCompleteExtender` | Students.aspx, Courses.aspx | **P2** | Consider BWFC implementation or document 3rd-party alternatives |
153+
| `ScriptManager` | Students.aspx, Courses.aspx, Instructors.aspx | **N/A** | Remove — not needed in Blazor |
154+
| `UpdatePanel` | Students.aspx, Courses.aspx, Instructors.aspx | **N/A** | Remove — Blazor handles this natively |
155+
156+
### Migration Script Recommendation
157+
158+
The Layer 1 migration script should:
159+
1. **Remove** `<asp:ScriptManager>` tags completely
160+
2. **Remove** `<asp:UpdatePanel>` wrappers (preserve `<ContentTemplate>` children)
161+
3. **Comment out** `<ajaxToolkit:*>` controls with a `<!-- BWFC: Unsupported control, manual migration required -->` warning
162+
119163
## Next Steps
120164

121165
1. **P0:** Fix Layer 1 script @onclick → OnClick transform

migration-toolkit/scripts/bwfc-migrate-layer2.ps1

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1174,6 +1174,7 @@ function Invoke-PatternC {
11741174
[void]$sb.AppendLine('')
11751175
[void]$sb.AppendLine('builder.Services.AddRazorComponents();')
11761176
[void]$sb.AppendLine('')
1177+
[void]$sb.AppendLine('builder.Services.AddHttpContextAccessor(); // Required for BWFC GridView/DetailsView')
11771178
[void]$sb.AppendLine('builder.Services.AddBlazorWebFormsComponents();')
11781179
[void]$sb.AppendLine('')
11791180

migration-toolkit/scripts/bwfc-migrate.ps1

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1533,22 +1533,22 @@ function Remove-WebFormsAttributes {
15331533
function ConvertFrom-ButtonOnClick {
15341534
<#
15351535
.SYNOPSIS
1536-
Converts Web Forms OnClick="HandlerName" to Blazor @onclick="HandlerName" on Button elements.
1536+
Preserves Web Forms OnClick="HandlerName" on BWFC Button elements.
15371537
.DESCRIPTION
15381538
In Web Forms, <asp:Button OnClick="btnSubmit_Click"> wires the button to a server-side handler.
1539-
In Blazor, this becomes <Button @onclick="btnSubmit_Click">.
1539+
BWFC Button components use OnClick as an EventCallback parameter, so the attribute is preserved
1540+
as-is (no conversion to @onclick needed).
15401541
1541-
This function finds OnClick attributes on Button/LinkButton/ImageButton elements and converts
1542-
them to @onclick. A manual item is logged reminding the developer to add the handler stub
1543-
in the code-behind if it doesn't already exist.
1542+
This function logs OnClick handlers found on Button/LinkButton/ImageButton elements and reminds
1543+
the developer to verify the handler signature in the code-behind.
15441544
#>
15451545
param(
15461546
[string]$Content,
15471547
[string]$RelPath
15481548
)
15491549

15501550
# Match OnClick="HandlerName" within Button, LinkButton, or ImageButton tags (after asp: prefix removal)
1551-
# This regex captures the handler name and replaces OnClick with @onclick
1551+
# BWFC uses OnClick as an EventCallback parameter, so we keep it as-is
15521552
$onClickRegex = [regex]'(<(?:Button|LinkButton|ImageButton)\s+[^>]*?)OnClick="([^"]+)"'
15531553
$onClickMatches = $onClickRegex.Matches($Content)
15541554

@@ -1558,13 +1558,13 @@ function ConvertFrom-ButtonOnClick {
15581558
$handlers += $m.Groups[2].Value
15591559
}
15601560

1561-
$Content = $onClickRegex.Replace($Content, '$1@onclick="$2"')
1562-
Write-TransformLog -File $RelPath -Transform 'ButtonHandler' -Detail "Converted $($onClickMatches.Count) OnClick to @onclick"
1561+
# No replacement needed - BWFC Button uses OnClick as EventCallback parameter
1562+
Write-TransformLog -File $RelPath -Transform 'ButtonHandler' -Detail "Found $($onClickMatches.Count) OnClick handler(s) — preserved for BWFC EventCallback"
15631563

15641564
# Log unique handlers as manual items for verification
15651565
$uniqueHandlers = $handlers | Select-Object -Unique
15661566
foreach ($h in $uniqueHandlers) {
1567-
Write-ManualItem -File $RelPath -Category 'ButtonHandler' -Detail "Converted OnClick=""$h"" → @onclick=""$h"". Verify handler exists in code-behind as: private void $h() { }"
1567+
Write-ManualItem -File $RelPath -Category 'ButtonHandler' -Detail "OnClick=""$h"" preserved. Verify handler exists in code-behind as: private void $h(MouseEventArgs e) { }"
15681568
}
15691569
}
15701570

migration-toolkit/skills/migration-standards/SKILL.md

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -52,12 +52,12 @@ The Layer 1 script's `Add-NavLinkIds` function handles this automatically.
5252

5353
```razor
5454
@* WRONG — direct child causes build error *@
55-
<GridView DataSource="@data">
55+
<GridView Items="@data">
5656
<BoundField DataField="Name" HeaderText="Name" />
5757
</GridView>
5858
5959
@* RIGHT — wrapped in Columns *@
60-
<GridView DataSource="@data">
60+
<GridView Items="@data">
6161
<Columns>
6262
<BoundField DataField="Name" HeaderText="Name" />
6363
</Columns>
@@ -440,12 +440,20 @@ The migration pipeline uses **two scripts** plus targeted manual overlay:
440440
- ReadOnly attribute warnings
441441
- Logout form → link conversion
442442

443-
**`-TestMode` switch:** Generates `ProjectReference` to local BWFC source instead of NuGet `PackageReference`, enabling rapid iteration during development runs:
443+
**`-TestMode` switch:** Generates `ProjectReference` to local BWFC source instead of NuGet `PackageReference`, enabling rapid iteration during development runs.
444+
445+
**`-BwfcProjectPath` parameter:** When using `-TestMode`, always specify the path to the local BWFC source:
444446

445447
```powershell
448+
# WRONG — TestMode without BwfcProjectPath may fail to locate BWFC source
446449
pwsh -File bwfc-migrate.ps1 -Path <source> -Output <target> -TestMode
450+
451+
# RIGHT — always specify BwfcProjectPath when testing in-repo samples
452+
pwsh -File bwfc-migrate.ps1 -Path <source> -Output <target> -TestMode -BwfcProjectPath "D:\BlazorWebFormsComponents\src\BlazorWebFormsComponents"
447453
```
448454

455+
**Database provider preference:** Maintain the original database provider from the source project unless explicitly directed otherwise. If the source uses SQL Server LocalDB, keep LocalDB in the migrated project. Only switch providers (e.g., to SQLite) when specifically requested.
456+
449457
#### Layer 2 — `bwfc-migrate-layer2.ps1`
450458

451459
The Layer 2 script targets three patterns of semantic transforms:

0 commit comments

Comments
 (0)