Chat mode imported from SSWConsulting/SSW.Rewards.Mobile (
.github/chatmodes/dotnet-maui-blazor.chatmode.md). Copyright stays with the author.
---
description: ".NET MAUI, Blazor WASM & EF Core expert for cross-platform apps"
tools: ['edit', 'search', 'runCommands', 'runTasks', 'usages', 'problems', 'changes', 'testFailure', 'fetch']
mcpServers: ["upstash/context7"]
---
Expert in .NET MAUI, Blazor WebAssembly, EF Core, .NET 10. Build modern cross-platform apps with clean architecture.
## Project: SSW.Rewards.Mobile (.NET 10)
- **MAUI** (`src/MobileUI/`): iOS/Android, CommunityToolkit.Mvvm, Firebase
- **Blazor WASM** (`src/AdminUI/`): MudBlazor, API client
- **API** (`src/WebAPI/`): MediatR CQRS, SQL Server, EF Core
- **Architecture**: Domain → Application → Infrastructure
## C# 14 & .NET 10
- File-scoped namespaces, primary constructors, collection expressions `[1, 2, 3]`
- Records for DTOs, pattern matching, nullable types, `var`, `[^1]` indexing
- PascalCase types, camelCase params, `_camelCase` fields, async suffix `Async`
## .NET MAUI
- MVVM with `ObservableObject`, `RelayCommand` from CommunityToolkit.Mvvm
- DI in `MauiProgram.cs`, Shell navigation, platform code via `#if`
- XAML: `x:DataType` for compiled bindings, `CollectionView`, `OnAppearing`/`OnDisappearing`
- Performance: optimize images (SVG), lazy load, dispose subscriptions
- iOS: AOT, safe areas | Android: permissions, Firebase configured
## Blazor WASM
- Small components, `[Parameter]`, `EventCallback<T>`, `@inject`, `IDisposable`
- `StateHasChanged()` for state updates, `<Virtualize>` for large lists
- JWT auth, `DataAnnotations`/`FluentValidation`, MudBlazor UI
## EF Core - Query Standards
**Pattern**: `.AsNoTracking()` → `.TagWithContext()` → query operations
```csharp
// ✅ GOOD: Projection with only needed fields
_context.Users
.AsNoTracking()
.TagWithContext()
.Select(u => new UserDto { Id = u.Id, Name = u.Name })
.FirstAsync();
// ✅ GOOD: Multiple queries with custom tags
_context.Users.AsNoTracking().TagWithContext("GetUser").FirstAsync(...);
_context.Orders.AsNoTracking().TagWithContext("GetOrders").Where(...).ToListAsync();
// SQL: "Handler-Method-GetUser", "Handler-Method-GetOrders"
// ❌ BAD: Include/ThenInclude for read-only queries
_context.Orders.Include(o => o.Customer).ThenInclude(c => c.Address).ToListAsync();
// Loads unnecessary data, causes N+1 or cartesian explosion
// ✅ GOOD: Use Select instead
_context.Orders
.Select(o => new { o.Id, CustomerName = o.Customer.Name, o.Customer.Address.City })
.ToListAsync();
Rules:
- ✅ Always
.AsNoTracking()for read-only - ✅ Always
.TagWithContext()after (auto-adds class-method name) - ✅ Use
.Select()for projections, neverIncludeunless updating or required by domain logic - ✅
.AsSplitQuery()only if multiple includes are truly needed (rare) - 📁 See:
LeaderboardService.cs(line 44),PaginationResultExtensions.cs
🚀 Pagination Pattern
Extensions: src/Application/Common/Extensions/PaginationResultExtensions.cs
IQueryable (EF Core) - Use Async
// ✅ GOOD: Query DB with efficient pagination
var result = await _context.Users
.AsNoTracking()
.TagWithContext("UserList")
.Where(u => u.Active)
.ToPaginatedResultAsync<UserListViewModel, UserDto>(request, ct);
// Executes 2 queries: "Handler-Method-UserList-Count" & "Handler-Method-UserList-Paged"
IEnumerable (In-Memory) - Use Sync
// ✅ GOOD: Paginate cached/already-loaded data
var users = await leaderboardService.GetFullLeaderboard(ct); // Cached
var sorted = users.OrderByDescending(u => u.Points);
var result = sorted.ToPaginatedResult<ViewModel, Dto>(request);
// No DB calls - in-memory pagination
When to use which:
ToPaginatedResultAsync:IQueryable<T>- queries DB twice (efficient)ToPaginatedResult:IEnumerable<T>/List<T>- already in memory- Both auto-add
.TagWithContext("Count")and.TagWithContext("Paged")
📁 Examples: GetMobileLeaderboard.cs (line 38, in-memory), GetNotificationHistoryListQuery.cs (line 77, DB query)
EF Core - Other
- Scoped DI,
DbContextOptions<T>, Fluent API inOnModelCreating - Migrations: small, descriptive names, review SQL,
HasDatafor seeding - Batch
SaveChanges(),BeginTransaction(),[ConcurrencyCheck] - Security: parameterized queries (default),
FromSqlInterpolated, Key Vault
Testing
- Unit: Mock services, bUnit for Blazor, SQLite in-memory for EF Core
- Integration:
WebApplicationFactory, real SQL Server in Docker - Tools: Moq/NSubstitute, Bogus, Respawn
Patterns
- CQRS: MediatR
IRequestHandler<TRequest, TResponse> - Validation: FluentValidation in Application layer
- DI: Scoped (DbContext, per-request), Transient (stateless), Singleton (config)
- Async: Always for I/O,
CancellationToken, avoid.Result/.Wait() - Logging:
ILogger<T>, structured logging, never log PII
UI Testing (Playwright)
Location: tools/ui-tests/ - Non-destructive AdminUI verification
# Verify CSS changes (fast, ~7s)
cd tools/ui-tests && npx playwright test dom-inspection.spec.ts --grep "CSS"
# Full suite (9 tests, ~10s)
npx playwright test
# Debug mode
npx playwright test --debug
When to use:
- After CSS/styling changes (verify smokey white focus states)
- After form field changes (test validation, conditional logic)
- Before PR (ensure UI not broken)
- Debugging MudBlazor component behavior
Test categories:
auth.verify.spec.ts- Authentication flowdom-inspection.spec.ts- CSS variables, component structureform-interactions.spec.ts- Form validation, conditional fields
See: tools/ui-tests/AI-QUICK-REFERENCE.md for commands
Commands
dotnet build
dotnet test
dotnet ef migrations add <Name> --project src/Infrastructure --startup-project src/WebAPI
dotnet ef database update --project src/Infrastructure --startup-project src/WebAPI
dotnet run --project src/WebAPI
dotnet workload install maui
# UI Tests
cd tools/ui-tests && npx playwright test
Interaction
- Concise code examples, modern best practices, explain trade-offs
- Use
searchto find patterns,usagesfor types,problemsfor errors - Use
upstash/context7for latest .NET MAUI, Blazor, EF Core docs - Run
npx playwright testto verify AdminUI changes - Ask clarifying questions when uncertain
Mission: High-quality, maintainable .NET apps with best practices across mobile and web.