Skip to content

Commit 2cd7cba

Browse files
csharpfritzCopilot
andcommitted
feat: non-generic GridViewRow, QueryString/RouteData attributes, IDE0007 suppression
- Add non-generic GridViewRow shim class for Web Forms compatibility (CS0305 fix) - Add [QueryString] and [RouteData] attributes targeting method parameters so L1 script preserves original Web Forms attributes instead of converting them - Add IDE0007 to BwfcMigrationMode warning suppression (style, not functional) - Update L1 script to preserve [QueryString]/[RouteData] (no conversion needed) - Eliminates CS0305 and CS0592 errors; 382 remaining are genuine L2 issues Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 77c2a26 commit 2cd7cba

7 files changed

Lines changed: 198 additions & 22 deletions

File tree

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
# WingtipToys Migration Test — Run 25
2+
3+
**Date:** 2026-03-26
4+
**Branch:** `feature/l1-migration-scaffold-improvements`
5+
**Type:** L1 — Non-generic GridViewRow, QueryString/RouteData attributes, IDE0007 suppression
6+
7+
## Summary
8+
9+
| Metric | Value |
10+
|--------|-------|
11+
| Source project | `samples/WingtipToys/WingtipToys` |
12+
| Output project | `samples/AfterWingtipToys` |
13+
| L1 script | `migration-toolkit/scripts/bwfc-migrate.ps1` |
14+
| **Total build errors** | **382** |
15+
| **Razor structural (RZ*)** | **18** |
16+
| **Genuine L2 (CS*)** | **364** |
17+
18+
### Error Count Correction
19+
20+
Run 24 reported "3 errors" — this was from an **incremental build** that cached
21+
successful compilation of unchanged files. A clean build with Models/ and Logic/
22+
files properly included produces 382 errors, all genuine L2 issues.
23+
24+
The L1 script's purpose is to produce compilable scaffolding for the parts it
25+
*can* transform. The 382 remaining errors are all in code that requires semantic
26+
understanding (L2/Copilot work):
27+
- Web Forms control IDs referenced as fields (no designer.cs in Blazor)
28+
- `Session`, `Context`, `User` page properties (need Blazor service injection)
29+
- Web Forms enums like `TextBoxMode`, `GridLines`, `BorderStyle`
30+
- `HttpUtility`, `FormsAuthentication` (Web Forms API surface)
31+
32+
## Changes in This Run
33+
34+
### 1. Non-generic `GridViewRow` Shim (BWFC Library)
35+
Web Forms `GridViewRow` is non-generic; BWFC's is `GridViewRow<T>`. Created a
36+
non-generic `GridViewRow` class with basic properties (`RowIndex`, `DataItemIndex`,
37+
`DataItem`). Marked `[Obsolete]` to guide L2 toward the generic version.
38+
39+
**Result:** CS0305 errors → **0** (was 2)
40+
41+
### 2. `[QueryString]` Attribute (BWFC Library)
42+
Instead of converting `[QueryString("id")]``[SupplyParameterFromQuery(Name = "id")]`
43+
(which fails because that attribute only targets properties, not method params), we now
44+
**leave `[QueryString]` as-is** and provide a BWFC `QueryStringAttribute` that targets
45+
`AttributeTargets.Parameter | AttributeTargets.Property`.
46+
47+
The L1 script no longer converts the attribute. L2 handles promotion to a proper
48+
`[Parameter, SupplyParameterFromQuery]` property on the component class.
49+
50+
**Result:** CS0592 errors → **0** (was 4)
51+
52+
### 3. `[RouteData]` Attribute (BWFC Library)
53+
Same pattern as `[QueryString]` — provide a BWFC `RouteDataAttribute` that targets
54+
method parameters so the original attribute compiles. L1 script no longer strips it.
55+
L2 promotes to `[Parameter]` property.
56+
57+
### 4. IDE0007 Suppression in Migration Mode
58+
Added `IDE0007` (use `var` instead of explicit type) to `BwfcMigrationMode`
59+
warning suppression. Web Forms code uses explicit types; this is a style concern
60+
that shouldn't block L1 builds.
61+
62+
**Result:** 198 fewer style errors
63+
64+
## Error Breakdown (382 total)
65+
66+
| Category | Count | Description |
67+
|----------|-------|-------------|
68+
| CS0103 | 256 | Missing name — control IDs, page properties, Web Forms APIs |
69+
| CS1503 | 26 | Type conversion mismatches |
70+
| CS1061 | 21 | Member not found on type |
71+
| CS0117 | 16 | No such member on type |
72+
| CS0246 | 15 | Type/namespace not found |
73+
| CS7036 | 14 | Required parameter not provided |
74+
| RZ9980 | 6 | Unclosed HTML tags from `<% %>` blocks |
75+
| CS0411 | 6 | Type arguments cannot be inferred |
76+
| RZ9996 | 2 | Unrecognized child content |
77+
| Other | 20 | Various (CS1525, CS0021, CS1929, RZ9981, etc.) |
78+
79+
### Top Missing Names (CS0103)
80+
81+
| Name | Count | Category |
82+
|------|-------|----------|
83+
| Context | 35 | Page property → inject IHttpContextAccessor |
84+
| User | 27 | Page property → inject AuthenticationStateProvider |
85+
| Session | 17 | Page property → inject ProtectedSessionStorage |
86+
| TextBoxMode | 16 | Web Forms enum → BWFC TextMode |
87+
| HttpUtility | 12 | System.Web → System.Net.WebUtility |
88+
| Control IDs | ~100 | Designer.cs fields → declare manually |
89+
90+
## What Worked Well
91+
92+
1. **"Leave it alone" pattern for attributes** — Creating BWFC shim attributes
93+
that accept the original Web Forms syntax is more robust than converting to
94+
Blazor equivalents at L1. Less script complexity, zero errors.
95+
2. **Non-generic GridViewRow** — C# allows both `GridViewRow` and `GridViewRow<T>`
96+
in the same namespace. Simple stub solves the compilation issue.
97+
3. **IDE0007 suppression** — Migrated code shouldn't be blocked by code style rules.
98+
99+
## What Needs Improvement (Future L1 Enhancements)
100+
101+
1. **Designer field generation** — The L1 script could parse `.aspx`/`.ascx` for
102+
`<asp:*>` controls with `ID="..."` and generate field declarations in the
103+
code-behind, similar to how Web Forms' designer.cs worked.
104+
2. **Page property stubs**`WebFormsPageBase` could provide `Session`, `Context`,
105+
`User`, `Request`, `Response`, `Server` properties that delegate to the proper
106+
ASP.NET Core services (inject via DI internally).
107+
3. **Web Forms enum aliases**`TextBoxMode`, `GridLines`, `BorderStyle` could be
108+
shimmed in BWFC's .targets via type aliases.
109+
4. **HttpUtility alias** — Add `<Using Alias="HttpUtility" Include="System.Net.WebUtility" />`
110+
to .targets (API is similar enough for common methods).

migration-toolkit/scripts/bwfc-migrate.ps1

Lines changed: 8 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1902,25 +1902,19 @@ function Copy-CodeBehind {
19021902
Write-TransformLog -File $RelPath -Transform 'CodeBehind' -Detail "Stripped $($baseClassMatches.Count) Web Forms base class declaration(s) — .razor @inherits handles inheritance"
19031903
}
19041904

1905-
# RF-12: Convert [QueryString] and [RouteData] parameter attributes
1906-
$qsRegex = [regex]'\[QueryString\("([^"]+)"\)\]'
1905+
# RF-12: [QueryString] and [RouteData] parameter attributes
1906+
# BWFC provides [QueryString] and [RouteData] attributes that target
1907+
# AttributeTargets.Parameter, so these compile as-is on method params.
1908+
# Leave them in place — L2 promotes them to component properties.
1909+
$qsRegex = [regex]'\[QueryString\('
19071910
$qsMatches = $qsRegex.Matches($annotatedContent)
19081911
if ($qsMatches.Count -gt 0) {
1909-
$annotatedContent = $qsRegex.Replace($annotatedContent, '[SupplyParameterFromQuery(Name = "$1")]')
1910-
Write-TransformLog -File $RelPath -Transform 'ParameterAttr' -Detail "Converted $($qsMatches.Count) [QueryString] to [SupplyParameterFromQuery]"
1912+
Write-TransformLog -File $RelPath -Transform 'ParameterAttr' -Detail "Preserved $($qsMatches.Count) [QueryString] attribute(s) — BWFC shim compiles; L2 promotes to [SupplyParameterFromQuery] property"
19111913
}
1912-
1913-
# [RouteData] is a Web Forms model-binding attribute for method parameters.
1914-
# It has no inline Blazor equivalent — [Parameter] targets properties, not
1915-
# method parameters, so placing it here would cause CS0592. Strip the
1916-
# attribute and leave a TODO for Layer 2 to promote the value to a
1917-
# [Parameter] property on the component class.
1918-
$rdRegex = [regex]'([ \t]*)\[RouteData\]\s*'
1914+
$rdRegex = [regex]'\[RouteData\]'
19191915
$rdMatches = $rdRegex.Matches($annotatedContent)
19201916
if ($rdMatches.Count -gt 0) {
1921-
$rdReplacement = '${1}/* TODO: RouteData parameter — add a [Parameter] property to the component class and remove from method signature */' + "`n" + '${1}'
1922-
$annotatedContent = $rdRegex.Replace($annotatedContent, $rdReplacement)
1923-
Write-TransformLog -File $RelPath -Transform 'ParameterAttr' -Detail "Stripped $($rdMatches.Count) [RouteData] attribute(s) — needs [Parameter] property in L2"
1917+
Write-TransformLog -File $RelPath -Transform 'ParameterAttr' -Detail "Preserved $($rdMatches.Count) [RouteData] attribute(s) — BWFC shim compiles; L2 promotes to [Parameter] property"
19241918
}
19251919

19261920
# --- Response.Redirect → NavigationManager.NavigateTo conversion ---
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
using System;
2+
3+
namespace BlazorWebFormsComponents;
4+
5+
/// <summary>
6+
/// Non-generic compatibility shim for Web Forms <c>System.Web.UI.WebControls.GridViewRow</c>.
7+
/// <para>
8+
/// Web Forms code often passes <c>GridViewRow</c> without a type parameter (e.g., in
9+
/// helper methods like <c>GetValues(GridViewRow row)</c>). The BWFC Blazor component is
10+
/// generic (<c>GridViewRow&lt;T&gt;</c>), so this non-generic class bridges the gap at L1.
11+
/// </para>
12+
/// <para>
13+
/// Layer 2 should refactor code to use the generic <c>GridViewRow&lt;T&gt;</c> component
14+
/// or replace the Web Forms row-manipulation pattern entirely.
15+
/// </para>
16+
/// </summary>
17+
[Obsolete("Use GridViewRow<T> for Blazor components. This non-generic shim exists for L1 migration compilation only.")]
18+
public class GridViewRow
19+
{
20+
public int RowIndex { get; set; }
21+
public int DataItemIndex { get; set; }
22+
public object DataItem { get; set; } = null!;
23+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
using System;
2+
3+
namespace BlazorWebFormsComponents;
4+
5+
/// <summary>
6+
/// Compatibility shim for Web Forms <c>[QueryString("paramName")]</c> model-binding attribute.
7+
/// <para>
8+
/// In Web Forms, <c>[QueryString]</c> decorated method parameters to bind them from the
9+
/// URL query string. In Blazor, this concept maps to <c>[SupplyParameterFromQuery]</c>
10+
/// on component <b>properties</b> (not method parameters).
11+
/// </para>
12+
/// <para>
13+
/// This attribute exists so migrated code-behinds compile at L1 without rewriting method
14+
/// signatures. Layer 2 should promote query-bound parameters to
15+
/// <c>[Parameter, SupplyParameterFromQuery]</c> properties on the component class.
16+
/// </para>
17+
/// </summary>
18+
[AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Property, AllowMultiple = false)]
19+
public sealed class QueryStringAttribute : Attribute
20+
{
21+
public QueryStringAttribute() { }
22+
23+
public QueryStringAttribute(string name)
24+
{
25+
Name = name;
26+
}
27+
28+
/// <summary>
29+
/// The query string parameter name to bind from.
30+
/// </summary>
31+
public string? Name { get; }
32+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
using System;
2+
3+
namespace BlazorWebFormsComponents;
4+
5+
/// <summary>
6+
/// Compatibility shim for Web Forms <c>[RouteData]</c> model-binding attribute.
7+
/// <para>
8+
/// In Web Forms, <c>[RouteData]</c> decorated method parameters to bind them from
9+
/// route data. In Blazor, this maps to <c>[Parameter]</c> properties on routable
10+
/// components with route template tokens (e.g., <c>@page "/product/{Id:int}"</c>).
11+
/// </para>
12+
/// <para>
13+
/// This attribute exists so migrated code-behinds compile at L1 without rewriting
14+
/// method signatures. Layer 2 should promote route-bound parameters to
15+
/// <c>[Parameter]</c> properties on the component class.
16+
/// </para>
17+
/// </summary>
18+
[AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Property, AllowMultiple = false)]
19+
public sealed class RouteDataAttribute : Attribute
20+
{
21+
}

src/BlazorWebFormsComponents/build/Fritz.BlazorWebFormsComponents.targets

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -46,10 +46,8 @@
4646
<!-- CS0414: Field assigned but never used (stub fields) -->
4747
<!-- CS0067: Event never used (stub events) -->
4848
<!-- CS0612: Obsolete type used (EF6 shims are [Obsolete]) -->
49-
<NoWarn>$(NoWarn);CS8618;CS0414;CS0067;CS0612</NoWarn>
50-
<!-- RZ9980/RZ9981: Unclosed/unexpected HTML tags from L1 transforms -->
51-
<!-- RZ9996: Unrecognized child content in BWFC components -->
52-
<NoWarn>$(NoWarn);RZ9980;RZ9981;RZ9996</NoWarn>
49+
<!-- IDE0007: Use var instead of explicit type (style, not functional) -->
50+
<NoWarn>$(NoWarn);CS8618;CS0414;CS0067;CS0612;IDE0007</NoWarn>
5351
</PropertyGroup>
5452

5553
</Project>

src/BlazorWebFormsComponents/buildTransitive/Fritz.BlazorWebFormsComponents.targets

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -46,10 +46,8 @@
4646
<!-- CS0414: Field assigned but never used (stub fields) -->
4747
<!-- CS0067: Event never used (stub events) -->
4848
<!-- CS0612: Obsolete type used (EF6 shims are [Obsolete]) -->
49-
<NoWarn>$(NoWarn);CS8618;CS0414;CS0067;CS0612</NoWarn>
50-
<!-- RZ9980/RZ9981: Unclosed/unexpected HTML tags from L1 transforms -->
51-
<!-- RZ9996: Unrecognized child content in BWFC components -->
52-
<NoWarn>$(NoWarn);RZ9980;RZ9981;RZ9996</NoWarn>
49+
<!-- IDE0007: Use var instead of explicit type (style, not functional) -->
50+
<NoWarn>$(NoWarn);CS8618;CS0414;CS0067;CS0612;IDE0007</NoWarn>
5351
</PropertyGroup>
5452

5553
</Project>

0 commit comments

Comments
 (0)