A focused .NET 10 Interactive Server companion demonstrating manual
EditContext ownership, DataAnnotations, FluentValidation, backend field-error
mapping, accessible reusable inputs, dirty-state tracking, save/discard behavior,
and bUnit component tests.
Blazor .NET 8 Forms & Validation: EditForm, FluentValidation & Server Error Handling
The tutorial explains Blazor forms on .NET 8.
This companion targets .NET 10 so it can use the DOTNET GUIDE repository's
current SDK and CI toolchain. The EditContext, ValidationMessageStore,
DataAnnotations, FluentValidation, field-error mapping, dirty-state,
reusable-input, and component-testing patterns demonstrated here are the same
core Blazor concepts.
- a Blazor Web App using Interactive Server;
- a manually created
EditContext; - DataAnnotations validation;
- FluentValidation 12 rules;
- a local FluentValidation-to-EditContext bridge;
- whole-model validation on submit;
- property-specific validation on field change;
- backend-returned field errors;
ValidationMessageStore;- clearing stale backend errors after field edits;
- reusable accessible text inputs;
aria-invalidandaria-describedby;- dirty-state detection with
IsModified(); - save and discard behavior;
MarkAsUnmodified()after successful save;- eight bUnit tests;
- one ASP.NET Core unknown-route integration test.
FluentValidation doesn't provide first-party Blazor integration.
The formerly common Blazored.FluentValidation adapter is archived.
This sample keeps the integration visible by using a small local component based on:
EditContext.OnValidationRequested;EditContext.OnFieldChanged;ValidationMessageStore;IValidator<T>;IncludeProperties.
The bridge supports synchronous FluentValidation rules only.
DataAnnotations
required, length, format
FluentValidation
conditional and cross-field rules
ProfileService
backend-only reserved username and blocked email-domain rules
Each source owns a separate validation-message store.
ProfileService is an in-process backend simulation.
It returns a dictionary of field names and messages so the component can demonstrate backend-error mapping.
It does not make HTTP requests or deserialize RFC 7807 responses.
The sample uses Interactive Server.
The browser must maintain an active Blazor circuit.
The profile service is scoped and stores data only in process memory.
Restarting the application resets the saved profile.
- .NET 10 SDK
- a modern browser for optional manual interaction checks
dotnet restore `
.\BlazorProfileValidation.slnx
dotnet build `
.\BlazorProfileValidation.slnx `
--configuration Release `
--no-restore
dotnet test `
.\BlazorProfileValidation.slnx `
--configuration Release `
--no-builddotnet run `
--project .\src\BlazorProfileValidation\BlazorProfileValidation.csproj `
--urls http://localhost:5144Open:
http://localhost:5144/
Use these valid client-side values to trigger backend-only errors:
Username: reserved
Email: any-address@blocked.example
The test suite contains:
- eight bUnit component tests;
- one ASP.NET Core integration test for direct unknown-route handling.
The local FluentValidation bridge supports explicitly declared cross-field dependencies (Username changes revalidate DisplayName). Dependencies are declared in the page component, not inferred automatically.
The tests do not launch a graphical browser or establish a real browser-driven SignalR session.
BlazorProfileValidation.slnx
README.md
src/
└── BlazorProfileValidation/
├── BlazorProfileValidation.csproj
├── Program.cs
├── Models/
│ └── ProfileModel.cs
├── Services/
│ └── ProfileService.cs
├── Validation/
│ └── ProfileValidator.cs
└── Components/
├── _Imports.razor
├── App.razor
├── Routes.razor
├── Pages/
│ ├── NotFound.razor
│ └── ProfileSettings.razor
├── Shared/
│ └── FormTextField.razor
└── Validation/
└── FluentValidationBridge.razor
tests/
└── BlazorProfileValidation.Tests/
├── BlazorProfileValidation.Tests.csproj
└── ProfileSettingsTests.cs
- third-party Blazor validation adapters;
- async validation;
- username API calls;
- debounce;
- JavaScript focus management;
- navigation guards;
- optimistic UI;
- authentication;
- databases;
- WebAssembly;
- browser automation;
- Docker;
- production persistence.
These topics remain in the complete tutorial.
- Companion target framework: .NET 10
- Tutorial framework: .NET 8
- Render mode: Interactive Server
- FluentValidation integration: local bridge
- External services required: none
- Database required: none
- API keys required: none
- Expected tests: 9
- Last reviewed: 2026-08-02
This sample is educational and should be reviewed before production use.