Blazor Web App (new)
(SPAs in Azure)
Blazor
natively treats app state as a first-class citizen. No
complex
serialization, API layer, or JS
state synchs required by React or Angular. By default, Blazor Web
Apps use Prerendering (Server
executes your state container, renders the HTML, and sends it to the
browser. Then, interactive connection kicks in, instantiates
a new state
container, and re-renders the page.).
Blazor
Web App (new)
Introduced
in .NET 8. Loads
initial page rapidly using Blazor Server, while downloading the
WebAssembly bundle in the background. Later
interactions use the browser's local resources. For a highly
stateful SPA, the Interactive
Auto render
mode solves the "slow initial load" problem that plagued
traditional client-side SPAs.
Steps when User Visits App:
1) Server instantly renders raw HTML/CSS (Static SSR) ----> User sees page instantly
2) Blazor Server spins up via SignalR (Interactive Server) -> User can immediately interact
3) Background downloads WebAssembly bundle (.NET runtime) -> Happens invisibly
4) On next visit, switches to client-side WebAssembly (Interactive WebAssembly) -> Zero server RAM cost now
DbContext Problems
If you use the Interactive Auto mode, your data-fetching and state logic must be location-agnostic. A standard DbContext injected directly into a component will crash. To build a stateful SPA in .NET 8+, you must abstract your data state behind an interface. You then register two different implementations of that interface: one for the Server project and one for the Client (WASM) project.
Shared interface:
public interface ICustomerStateService {
List<CustomerDto> Customers { get; }
event Action? OnStateChanged;
Task LoadCustomersAsync(); }
Server:
public class ServerCustomerStateService : ICustomerStateService {
private readonly MyDbContext _context; // Direct database access
public List<CustomerDto> Customers { get; private set; } = new();
public event Action? OnStateChanged;
public ServerCustomerStateService(MyDbContext context) => _context = context;
public async Task LoadCustomersAsync()
{
// Fetch straight from the database for the initial fast load
Customers = await _context.Customers.ToListAsync();
OnStateChanged?.Invoke();
} }
Browser Client:
public class ClientCustomerStateService : ICustomerStateService {
private readonly HttpClient _http; // Network access
public List<CustomerDto> Customers { get; private set; } = new();
public event Action? OnStateChanged;
public ClientCustomerStateService(HttpClient http) => _http = http;
public async Task LoadCustomersAsync()
{
// Once running on the browser, fetch via secure Web API endpoints
Customers = await _http.GetFromJsonAsync<List<CustomerDto>>("api/customers") ?? new();
OnStateChanged?.Invoke();
} }
Register Them Separately in Program.cs
- In the Server Project's
Program.cs:
builder.Services.AddScoped<ICustomerStateService, ServerCustomerStateService>(); - In the Client Project's
Program.cs:
builder.Services.AddScoped<ICustomerStateService, ClientCustomerStateService>();
Comments
Post a Comment