Services
Components should display things and react to users. Services do the work — loading data, calculating totals, holding shared state.
On this page · 5 min · Intermediate
On this page
- Learning objectives
- Explanation
- Code example
- Step by step
- Real-world use
- Common mistakes
- Best practices
- Remember this
- Practice exercise
- Mini quiz
- Related topics
- My notes
- Reading time
- 5 min
- Difficulty
- Intermediate
- Updated
- Oct 29, 2026
Interview questions
Learning objectives#
- Move business logic into services
- Design interfaces that work on server and WebAssembly
- Build a state container service
- Test services without a UI
Explanation#
A service is a plain C# class registered in DI. Components inject it and stay thin.
For apps that use both Interactive Server and WebAssembly, define an interface (IProductService) with two implementations: one that uses EF Core on the server, one that calls the API with HttpClient in the client.
A state container is a scoped service holding shared UI state (cart, filters) and raising an OnChange event.
Code example: A cart state container#
public class CartState
{
private readonly List<CartLine> lines = [];
public IReadOnlyList<CartLine> Lines => lines;
public decimal Total => lines.Sum(l => l.Price * l.Qty);
public event Action? OnChange;
public void Add(Product p)
{
var line = lines.FirstOrDefault(l => l.ProductId == p.Id);
if (line is null) lines.Add(new CartLine(p.Id, p.Name, p.Price, 1));
else line.Qty++;
OnChange?.Invoke();
}
}
public class CartLine(int productId, string name, decimal price, int qty)
{
public int ProductId { get; } = productId; public string Name { get; } = name;
public decimal Price { get; } = price; public int Qty { get; set; } = qty;
}
// Program.cs: builder.Services.AddScoped<CartState>();
// CartBadge.razor: @inject CartState Cart → subscribe in OnInitialized: Cart.OnChange += StateHasChanged;
// and unsubscribe in Dispose: Cart.OnChange -= StateHasChanged;
Adding a laptop on the product page instantly updates the cart badge in the header to “1 · ₹55,000”.
This example is illustrative. Copy or download it and run it in your own environment; the expected output is shown above.
Step-by-step explanation#
- The service owns the cart lines and the total calculation.
- Add raises OnChange after every change.
- Any component can subscribe and call StateHasChanged.
- Unsubscribing in Dispose prevents memory leaks.
Real-world use case#
A header cart badge and the product page stay in sync without passing parameters through every layout.
Common mistakes#
All users would share one cart. Use Scoped.
Use InvokeAsync(StateHasChanged) when the event may come from another thread.
Best practices#
- Thin components, rich services
- Interfaces for render-mode-specific implementations
- Unit test services directly
- Unsubscribe from events in Dispose
Remember this#
Components show; services do.
State container = scoped service + OnChange event.
Subscribe in OnInitialized, unsubscribe in Dispose.
Practice exercise#
Your task: Add a Remove(int productId) method to CartState.
Show solution
public void Remove(int productId)
{
lines.RemoveAll(l => l.ProductId == productId);
OnChange?.Invoke();
}
Mini quiz#
Which lifetime suits a per-user cart in Interactive Server?
Want more? Practise all Blazor MCQs · Blazor interview questions
My notes#
Saved in this browser.