fix(web): include PublicationMode/IsMembersOnly in showcase SQL (v3.7.1) #120

Closed
Toutsu wants to merge 0 commits from fix/issue-110-showcase-500 into main
Owner

🐛 Hotfix: /showcase возвращает 500 после v3.7.0

Что случилось

После выкатки v3.7.0 (Issue #110) страница /showcase падает с 500 — app.css грузится, логин есть, на самом рендере ASP.NET роняет SSR-цикл Blazor.

Корневая причина

В v3.7.0 я расширил ShowcaseSessionDto (и DTO-тип ShowcaseSessionRow в SessionService.cs), добавив в конец PublicationMode и IsMembersOnly. Но в SELECT-ах GetShowcaseSessionsAsync / GetShowcaseSessionAsync эти колонки не отдавались — старые 19 полей.

Dapper.AOT по SELECT-у генерирует требуемую сигнатуру конструктора для материализации. Получилось расхождение: row-record ожидает 21 параметр, Dapper присылает 19 → InvalidOperationException: A parameterless default constructor or one matching signature (...) is required for GmRelay.Web.Services.ShowcaseSessionRow materialization.

Фикс

  • Добавил в оба SELECT-а в конец: s.publication_mode AS PublicationMode, (s.publication_mode = 'ClubOnly') AS IsMembersOnly.
  • В new ShowcaseSessionDto(...) в обоих методах PublicationMode: "Catalog" / IsMembersOnly: false заменил на r.PublicationMode / r.IsMembersOnly (всё равно это и было целью).

Стек из лога прод-контейнера

fail: Microsoft.AspNetCore.Diagnostics.ExceptionHandlerMiddleware[1]
      An unhandled exception has occurred while executing the request.
      System.InvalidOperationException: A parameterless default constructor or
      one matching signature (... 19 параметров ...) is required for
      GmRelay.Web.Services.ShowcaseSessionRow materialization
         at GmRelay.Web.Services.SessionService.GetShowcaseSessionsAsync(...) line 453
         at GmRelay.Web.Components.Pages.Showcase.LoadAsync() line 338
         at GmRelay.Web.Components.Pages.Showcase.OnInitializedAsync() line 328

Верификация

  • dotnet build src/GmRelay.Web/GmRelay.Web.csproj — 0 warnings, 0 errors.
  • dotnet test — 493/493 passed.
  • dotnet format --verify-no-changes — exit 0.

Версионирование

  • Bump 3.7.0 → 3.7.1 (patch — bugfix без миграций и без API-изменений).
  • Directory.Build.props, compose.yaml (3 image refs), .gitea/workflows/deploy.yml, src/GmRelay.Web/Components/Layout/NavMenu.razor, tests/GmRelay.Bot.Tests/Web/CampaignTemplatesNavigationTests.cs — синхронизированы.

Workflow

  • CI passes
  • Code review approved
  • Deployed
  • Release published
## 🐛 Hotfix: /showcase возвращает 500 после v3.7.0 ### Что случилось После выкатки v3.7.0 (Issue #110) страница `/showcase` падает с 500 — `app.css` грузится, логин есть, на самом рендере ASP.NET роняет SSR-цикл Blazor. ### Корневая причина В v3.7.0 я расширил `ShowcaseSessionDto` (и DTO-тип `ShowcaseSessionRow` в `SessionService.cs`), добавив в конец `PublicationMode` и `IsMembersOnly`. Но в SELECT-ах `GetShowcaseSessionsAsync` / `GetShowcaseSessionAsync` эти колонки **не отдавались** — старые 19 полей. Dapper.AOT по SELECT-у генерирует требуемую сигнатуру конструктора для материализации. Получилось расхождение: row-record ожидает 21 параметр, Dapper присылает 19 → `InvalidOperationException: A parameterless default constructor or one matching signature (...) is required for GmRelay.Web.Services.ShowcaseSessionRow materialization`. ### Фикс - Добавил в оба SELECT-а в конец: `s.publication_mode AS PublicationMode, (s.publication_mode = 'ClubOnly') AS IsMembersOnly`. - В `new ShowcaseSessionDto(...)` в обоих методах `PublicationMode: "Catalog" / IsMembersOnly: false` заменил на `r.PublicationMode / r.IsMembersOnly` (всё равно это и было целью). ### Стек из лога прод-контейнера ``` fail: Microsoft.AspNetCore.Diagnostics.ExceptionHandlerMiddleware[1] An unhandled exception has occurred while executing the request. System.InvalidOperationException: A parameterless default constructor or one matching signature (... 19 параметров ...) is required for GmRelay.Web.Services.ShowcaseSessionRow materialization at GmRelay.Web.Services.SessionService.GetShowcaseSessionsAsync(...) line 453 at GmRelay.Web.Components.Pages.Showcase.LoadAsync() line 338 at GmRelay.Web.Components.Pages.Showcase.OnInitializedAsync() line 328 ``` ### Верификация - `dotnet build src/GmRelay.Web/GmRelay.Web.csproj` — 0 warnings, 0 errors. - `dotnet test` — 493/493 passed. - `dotnet format --verify-no-changes` — exit 0. ### Версионирование - Bump 3.7.0 → 3.7.1 (patch — bugfix без миграций и без API-изменений). - `Directory.Build.props`, `compose.yaml` (3 image refs), `.gitea/workflows/deploy.yml`, `src/GmRelay.Web/Components/Layout/NavMenu.razor`, `tests/GmRelay.Bot.Tests/Web/CampaignTemplatesNavigationTests.cs` — синхронизированы. ## Workflow - [ ] CI passes - [ ] Code review approved - [ ] Deployed - [ ] Release published
Toutsu added 1 commit 2026-06-03 22:23:17 +03:00
fix(web): include PublicationMode/IsMembersOnly in showcase SQL to fix /showcase 500
PR Checks / test-and-build (pull_request) Successful in 8m17s
29f6f6a827
Dapper.AOT generated a 19-parameter ctor for ShowcaseSessionRow based on the
SELECT list in GetShowcaseSessionsAsync / GetShowcaseSessionAsync. After
adding PublicationMode and IsMembersOnly to ShowcaseSessionDto in v3.7.0 the
record itself was extended, but the SELECT still returned 19 columns, so the
materializer threw "A parameterless default constructor or one matching
signature (...) is required" and every request to /showcase returned 500.

Add s.publication_mode and (s.publication_mode = 'ClubOnly') to both SELECT
lists and propagate them through the ShowcaseSessionDto construction. The
field list now matches the generated constructor exactly.

Version bump 3.7.0 -> 3.7.1 (patch).
Toutsu closed this pull request 2026-06-03 22:32:47 +03:00
Some checks are pending
PR Checks / test-and-build (pull_request) Successful in 8m17s

Pull request closed

Sign in to join this conversation.
No Reviewers
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: Toutsu/GmRelayBot#120