|
| 1 | +# ContosoUniversity Migration Run 07 |
| 2 | + |
| 3 | +**Date:** 2026-03-09 |
| 4 | +**Branch:** `squad/audit-docs-perf` |
| 5 | +**Commit:** `f08c7a6f` |
| 6 | +**Score:** **40/40 (100%)** ✅ |
| 7 | + |
| 8 | +## Executive Summary |
| 9 | + |
| 10 | +Run 07 of the ContosoUniversity migration achieved 100% acceptance test passage (40/40). The migration scripts (Layer 1 + Layer 2) completed in under 2 seconds total. Manual fixes required ~20 minutes, primarily addressing a BWFC DetailsView incompatibility and Blazor circuit timing issues in tests. |
| 11 | + |
| 12 | +## Timing |
| 13 | + |
| 14 | +| Phase | Duration | Notes | |
| 15 | +|-------|----------|-------| |
| 16 | +| **Layer 1 (markup)** | 0.99 seconds | 72 transforms, 6 files | |
| 17 | +| **Layer 2 (code-behind)** | 0.40 seconds | 7 transforms | |
| 18 | +| **Manual fixes** | ~20 minutes | Build errors, model fixes, test timing | |
| 19 | +| **Total automated** | **1.39 seconds** | | |
| 20 | + |
| 21 | +## Test Results |
| 22 | + |
| 23 | +| Category | Passed | Failed | Notes | |
| 24 | +|----------|--------|--------|-------| |
| 25 | +| About Page | 5/5 | 0 | Enrollment stats display | |
| 26 | +| Instructors Page | 5/5 | 0 | GridView with sorting | |
| 27 | +| Courses Page | 6/6 | 0 | Search + DetailsView fix | |
| 28 | +| Students Page | 9/9 | 0 | CRUD operations | |
| 29 | +| Navigation | 7/7 | 0 | Routing + nav links | |
| 30 | +| Home Page | 8/8 | 0 | Welcome page | |
| 31 | +| **TOTAL** | **40/40** | **0** | **100%** | |
| 32 | + |
| 33 | +## Issues Discovered & Fixed |
| 34 | + |
| 35 | +### 1. DetailsView BoundField Incompatibility (Critical) |
| 36 | + |
| 37 | +**Problem:** BWFC `DetailsView` uses an internal field system (`DetailsViewField` subclasses) rather than the standard `BoundField` component used by `GridView`. Placing `BoundField` inside `<Fields>` causes: |
| 38 | +``` |
| 39 | +NullReferenceException: Object reference not set to an instance of an object. |
| 40 | + at BlazorWebFormsComponents.BaseColumn`1.OnInitialized() |
| 41 | +``` |
| 42 | + |
| 43 | +**Root Cause:** `BoundField` looks for a `CascadingParameter(Name = "ColumnCollection")` but `DetailsView` provides `DetailsViewFieldCollection` instead. |
| 44 | + |
| 45 | +**Solution:** Replaced DetailsView with plain HTML table: |
| 46 | +```razor |
| 47 | +<!-- Before (broken) --> |
| 48 | +<DetailsView ID="dtlCourses" DataItem="_selectedCourse"> |
| 49 | + <Fields> |
| 50 | + <BoundField DataField="CourseID" HeaderText="Course ID" /> |
| 51 | + </Fields> |
| 52 | +</DetailsView> |
| 53 | +
|
| 54 | +<!-- After (working) --> |
| 55 | +<table id="dtlCourses" class="details"> |
| 56 | + <tr><td>Course ID</td><td>@_selectedCourse.CourseID</td></tr> |
| 57 | +</table> |
| 58 | +``` |
| 59 | + |
| 60 | +**Recommendation:** Document that DetailsView `<Fields>` parameter expects `DetailsViewAutoField` or custom implementations, NOT `BoundField`. |
| 61 | + |
| 62 | +### 2. Button OnClick Handler Signature |
| 63 | + |
| 64 | +**Problem:** Button `OnClick` handlers in InteractiveServer mode need `MouseEventArgs` parameter: |
| 65 | +```csharp |
| 66 | +// Wrong - causes EventCallback type mismatch |
| 67 | +private async Task SearchCourseByName() { } |
| 68 | + |
| 69 | +// Correct |
| 70 | +private async Task SearchCourseByName(MouseEventArgs args) { } |
| 71 | +``` |
| 72 | + |
| 73 | +**Solution:** Added `using Microsoft.AspNetCore.Components.Web` and `MouseEventArgs` to handler signatures. |
| 74 | + |
| 75 | +### 3. Nullable String Properties |
| 76 | + |
| 77 | +**Problem:** Database has NULL values but models had non-nullable `string` with `= ""` initializers, causing `SqlNullValueException`. |
| 78 | + |
| 79 | +**Solution:** Changed all string properties to `string?`: |
| 80 | +```csharp |
| 81 | +public string? CourseName { get; set; } |
| 82 | +``` |
| 83 | + |
| 84 | +### 4. LINQ Contains with Null Check |
| 85 | + |
| 86 | +**Problem:** `Contains()` query on nullable string column fails: |
| 87 | +```csharp |
| 88 | +// Wrong |
| 89 | +c.CourseName.Contains(_searchCourse) |
| 90 | + |
| 91 | +// Correct |
| 92 | +c.CourseName != null && c.CourseName.Contains(_searchCourse) |
| 93 | +``` |
| 94 | + |
| 95 | +### 5. Playwright Test Timing for Blazor |
| 96 | + |
| 97 | +**Problem:** Tests for InteractiveServer pages fail because Blazor SignalR circuit isn't established before interactions. |
| 98 | + |
| 99 | +**Solution:** Added explicit waits: |
| 100 | +```csharp |
| 101 | +await page.WaitForLoadStateAsync(LoadState.NetworkIdle); |
| 102 | +await page.WaitForTimeoutAsync(1000); // Wait for Blazor circuit |
| 103 | +// ... interact ... |
| 104 | +await page.WaitForTimeoutAsync(500); // Wait for re-render |
| 105 | +``` |
| 106 | + |
| 107 | +## Files Modified |
| 108 | + |
| 109 | +### New Files Created |
| 110 | +- `Models/ContosoUniversityContext.cs` - EF Core DbContext with SQL Server |
| 111 | +- `Pages/About.razor` + `.cs` - About page with enrollment stats |
| 112 | +- `Pages/Courses.razor` + `.cs` - Courses page with search |
| 113 | +- `Pages/Home.razor` - Home page |
| 114 | +- `Pages/Instructors.razor` + `.cs` - Instructors with GridView sorting |
| 115 | +- `Pages/Students.razor` + `.cs` - Student CRUD operations |
| 116 | +- `appsettings.Development.json` - Detailed error logging |
| 117 | + |
| 118 | +### Modified Files |
| 119 | +- `ContosoUniversity.csproj` - Added EF Core SQL Server package |
| 120 | +- `Program.cs` - DbContextFactory, URL rewriting, middleware |
| 121 | +- `_Imports.razor` - Added model namespace |
| 122 | +- `Components/Layout/MainLayout.razor` - Fixed nav links |
| 123 | + |
| 124 | +### Test Files Modified |
| 125 | +- `src/ContosoUniversity.AcceptanceTests/CoursesPageTests.cs` - Added Blazor circuit timing waits |
| 126 | + |
| 127 | +## Key Learnings |
| 128 | + |
| 129 | +1. **BWFC DetailsView is not column-compatible with GridView** - They share similar syntax but use different internal field systems |
| 130 | +2. **InteractiveServer handlers need MouseEventArgs** - EventCallback<MouseEventArgs> requires the parameter |
| 131 | +3. **Playwright tests for Blazor need explicit timing** - NetworkIdle isn't enough; must wait for SignalR circuit |
| 132 | +4. **SQL Server nullable columns need nullable C# types** - Even with empty string initializers, EF Core can return null |
| 133 | + |
| 134 | +## Comparison to Previous Runs |
| 135 | + |
| 136 | +| Run | Score | Key Improvement | |
| 137 | +|-----|-------|----------------| |
| 138 | +| Run 01-03 | 31/40 (77.5%) | Initial migration | |
| 139 | +| Run 04 | 36/40 (90%) | URL rewriting | |
| 140 | +| Run 05 | 40/40 (100%) | SQL Server, InteractiveServer | |
| 141 | +| **Run 07** | **40/40 (100%)** | DetailsView fix, test timing | |
| 142 | + |
| 143 | +## Recommendations |
| 144 | + |
| 145 | +1. **Update migration skills** to document DetailsView limitations |
| 146 | +2. **Add DetailsViewBoundField component** to BWFC library (or document the limitation) |
| 147 | +3. **Update Layer 2 script** to add MouseEventArgs to button handlers automatically |
| 148 | +4. **Add Blazor circuit wait helper** to acceptance test infrastructure |
0 commit comments