12 KiB
Team Brief — AuthBlocks: Register ModelView's Missing Dependency via ConfigureAuthServices
Audience: an orchestrator (and its implementers) working only in the AuthBlocks repository at
C:\Development\AuthBlocks. You do not need, and should not assume, any knowledge of the products that
consume AuthBlocks. Everything you need is in this brief or in that one repo (plus a single new BlazorBlocks
package version — see §2, the blocking prerequisite).
Status: ✅ RESOLVED — shipped in Cerebellum.BlazorBlocks.Web 10.3.33 + Cerebellum.AuthBlocks.Web 10.3.36 (2026-06-20). scoped request, blocked on a BlazorBlocks publish (see §2). Confirmed at runtime against Cerebellum.AuthBlocks.Web 10.3.33 / Cerebellum.BlazorBlocks.Web 10.3.32. Author: product-designer (for a downstream consumer team). Date: 2026-06-19.
Resolution (2026-06-20):
AddBlazorBlocksWeb()landed inCerebellum.BlazorBlocks.Web10.3.33 andConfigureAuthServicescalls it inCerebellum.AuthBlocks.Web10.3.36; DeepDrftManager picked up 10.3.36 and removed its localEditModalSaveContextHolderstopgap. This brief is retained as historical record — no further action required.
This is one half of a two-team, ordered fix. AuthBlocks ships second — see §2 and §9.
1. The defect in one sentence
AuthBlocksWeb ships Users.razor and Registrations.razor, both of which render BlazorBlocks'
<ModelView> component — but ConfigureAuthServices (the single DI entry point consumers call to light up
the AuthBlocks Web surface) does not ensure ModelView's required service
Web.Maintenance.Entities.EditModalSaveContextHolder is registered. So a consumer that wires up AuthBlocks
the normal way gets an unhandled InvalidOperationException that terminates the Blazor circuit on
navigation to either page, unless that consumer manually hand-registers an internal BlazorBlocks service
in its own Program.cs.
The fix: have ConfigureAuthServices call BlazorBlocks' new AddBlazorBlocksWeb() extension (which
registers the holder), so AuthBlocks stays self-contained for its consumers.
2. Blocking prerequisite — BlazorBlocks must ship first
This fix cannot land until BlazorBlocks publishes a new Cerebellum.BlazorBlocks.Web version that
exposes a Web-side registration extension (working name AddBlazorBlocksWeb()). That extension is what
actually registers EditModalSaveContextHolder; AuthBlocks' job is only to call it.
- AuthBlocks is currently on
Cerebellum.BlazorBlocks.Web10.3.32, which has no such extension. - The BlazorBlocks team is shipping the extension and bumping the package as the first half of this fix.
- Reference the specific new version BlazorBlocks publishes for this fix — fill in the exact version number once the BlazorBlocks team reports it. Do not proceed against 10.3.32; the method will not exist.
If you reach this work before the BlazorBlocks version is available, stop and wait for the published version number. The AuthBlocks change is small once the prerequisite is in hand.
3. The confirmed failure
Stack trace (captured from a consuming app on navigation to the Users admin page)
System.InvalidOperationException: Cannot provide a value for property 'SaveContextHolder' on type
'Web.Maintenance.Entities.ModelView`5[[AuthBlocksModels.InputModels.UserInputModel, ...],
[AuthBlocksModels.Models.UserModel, ...],[AuthBlocksWeb.Components.Pages.UserAdmin.Users.UserEditModal, ...],
[AuthBlocksWeb.Components.Pages.UserAdmin.Users.UsersViewModel, ...],
[AuthBlocksModels.Converters.UserModelToInputConverter, ...]]'.
There is no registered service of type 'Web.Maintenance.Entities.EditModalSaveContextHolder'.
at Microsoft.AspNetCore.Components.ComponentFactory...CreatePropertyInjector...
ModelView declares SaveContextHolder as required with [Inject], so Blazor's component factory
throws during component activation. There is no try/catch around component instantiation in the render path,
so the exception propagates and tears down the circuit. The user sees a dead page / "An unhandled error has
occurred" and must reload.
Which AuthBlocks pages trigger it
AuthBlocksWeb ships two user-admin pages that render <ModelView>:
AuthBlocksWeb/Components/Pages/UserAdmin/Users/Users.razorAuthBlocksWeb/Components/Pages/UserAdmin/Registrations/Registrations.razor
Any consumer that routes to either page hits the defect on first navigation.
4. Why this is AuthBlocks' gap to close (not the consumer's)
AuthBlocksWeb/Startup.cs exposes ConfigureAuthServices(IServiceCollection, string apiBaseUrl) — the
single DI entry point consumers call to light up the AuthBlocks Web surface. It already registers all
the user-admin viewmodels and clients (UsersViewModel, RegistrationsViewModel, PermissionsViewModel,
their clients, auth state, hierarchical authorization, etc.). It does not register
EditModalSaveContextHolder and does not call any BlazorBlocks Web extension.
So AuthBlocks ships pages that depend on ModelView while leaving one of ModelView's required services
unregistered. The consumer is silently expected to fill the gap — and that is exactly what happened:
two independent downstream products each had to hand-register the internal BlazorBlocks service
(AddScoped<Web.Maintenance.Entities.EditModalSaveContextHolder>()) in their own Program.cs to make
AuthBlocks' shipped pages work. The whole value of a single ConfigureAuthServices entry point is that a
consumer calling it (plus the already-required AddMudServices) gets a working surface with zero manual
registrations. Today they don't. Closing this gap inside ConfigureAuthServices restores that promise.
Note: IDialogService / ISnackbar (also injected by ModelView) come from MudBlazor's
AddMudServices(), which every AuthBlocks Web consumer already calls as a documented prerequisite — those
are not the gap. The single library-owned gap is EditModalSaveContextHolder.
5. The fix
5.1 Bump the BlazorBlocks reference
Update the Cerebellum.BlazorBlocks.Web package reference (currently 10.3.32) to the new version
BlazorBlocks publishes for this fix (see §2 — fill in the exact version). This is what makes
AddBlazorBlocksWeb() available.
5.2 Call the extension from ConfigureAuthServices
// AuthBlocksWeb/Startup.cs, inside ConfigureAuthServices(...)
services.AddBlazorBlocksWeb(); // registers EditModalSaveContextHolder for the ModelView-based pages
Order does not matter for this scoped service; place it near the other registrations. The
AddBlazorBlocksWeb() extension registers EditModalSaveContextHolder as scoped per circuit (its correct
lifetime — the holder is per-circuit mutable state that ModelView writes and EditModelModal reads).
5.3 Why this belongs in ConfigureAuthServices, not pushed onto consumers
ConfigureAuthServicesis AuthBlocks' single DI entry point. A consumer calling onlyAddMudServices()+ConfigureAuthServices(...)should get a fully working user-admin surface. Folding the BlazorBlocks call into the existing entry point keeps that promise; introducing a second method the consumer must remember to call (or expecting them to callAddBlazorBlocksWeb()themselves) just relocates the leaked registration one layer up.- AuthBlocks must not register
EditModalSaveContextHolderdirectly (reaching into BlazorBlocks' internalWeb.Maintenance.Entitiesnamespace) — that is the same smell the two consumers exhibited, merely relocated. Compose BlazorBlocks' ownAdd*extension instead; the registration lives with its owner, and AuthBlocks stays self-contained by calling it. This is the standard ASP.NET Core layering: each library exposes anAdd*for its own services, and a higher-level library'sAdd*calls the lower one's.
6. Constraints
- Do not register
EditModalSaveContextHolderdirectly in AuthBlocks. CallAddBlazorBlocksWeb(); let BlazorBlocks own its type (§5.3). - Keep
ConfigureAuthServicesthe single AuthBlocks entry point. Do not introduce a second method consumers must remember to call; fold the BlazorBlocks call into the existing one. - MudBlazor remains a caller-owned prerequisite. AuthBlocks already relies on consumers calling
AddMudServices()for all its MudBlazor-based pages; do not absorb it intoConfigureAuthServices. - Versioning: AuthBlocks Web packs/pushes via its
pack.ps1/ packaging script.AuthBlocksWebis currentlyCerebellum.AuthBlocks.Web10.3.33; bump to the next version after referencing the new BlazorBlocks version and adding theAddBlazorBlocksWeb()call. Record the new version so consumers can pin.
7. Acceptance criteria
- The
Cerebellum.BlazorBlocks.Webpackage reference is bumped to the new version BlazorBlocks published for this fix, andConfigureAuthServicescallsAddBlazorBlocksWeb(). - A fresh consumer that calls only
AddMudServices()andAuthBlocksWeb.Startup.ConfigureAuthServices(...)— and registers nothing else by hand — can navigate to the Users admin page and the Registrations admin page with noInvalidOperationExceptionand no circuit teardown. - Working behavior means not just page load: opening the edit dialog on a user, saving a valid change, succeeds — i.e. the save bridge actually works end-to-end through AuthBlocks' pages.
- No AuthBlocks consumer needs to touch
Web.Maintenance.Entitiesdirectly; no manual registrations are required beyond the documentedAddMudServices. AuthBlocksWebis published as a version bump from 10.3.33, and the new version number is recorded.
8. Open questions for the implementing team / its sponsor
- Exact BlazorBlocks version to reference. Pending the BlazorBlocks team's publish (§2). Confirm the
published version number and the exact extension method name (proposed
AddBlazorBlocksWeb()) before landing the call. - Placement within
ConfigureAuthServices. Anywhere in the method works (scoped service, order- independent). Confirm there is no existing convention inStartup.csfor grouping third-partyAdd*calls that this should follow. - Any other AuthBlocks pages built on
ModelView/NewModelView? This brief identified Users and Registrations. If the team expects to add more maintenance pages, note that callingAddBlazorBlocksWeb()once covers them all (it is the single home for the maintenance-component deps).
9. Suggested reading order in the repo
AuthBlocksWeb/Startup.cs—ConfigureAuthServices, the single entry point; add theAddBlazorBlocksWeb()call here.AuthBlocksWeb/Components/Pages/UserAdmin/Users/Users.razor— renders<ModelView>; triggers the defect on navigation.AuthBlocksWeb/Components/Pages/UserAdmin/Registrations/Registrations.razor— the second page that renders<ModelView>.- The AuthBlocks Web
.csproj— theCerebellum.BlazorBlocks.Webpackage reference to bump. - The AuthBlocks
pack.ps1/ packaging script — to bump and publishAuthBlocksWebafter referencing the new BlazorBlocks version.
10. Cross-team ordering (important — you ship second)
This fix is layered across two repos and must land in order:
- BlazorBlocks ships first. It adds
AddBlazorBlocksWeb(), bumpsCerebellum.BlazorBlocks.Webfrom 10.3.32, packs/pushes, and reports the new version number. - Then AuthBlocks (this team) bumps its
Cerebellum.BlazorBlocks.Webreference to that published version, callsAddBlazorBlocksWeb()fromConfigureAuthServices, bumpsCerebellum.AuthBlocks.Webfrom 10.3.33, and publishes.
This team cannot complete its part until the BlazorBlocks version is published (§2). Confirm that version number is in hand before starting.